XMLHttpRequest not working with FileReader - javascript

I have a form in which the user can upload images and pdf-files. Right now I'm trying to convert the pdf-files into jpg-files, in order to be able to preview them BEFORE the form gets submitted.
Here is the form:
<form>
<div class="fileUpload">
<p class="fileformats">Valid formats: jpg/png/gif/jpeg/pdf</p><br>
<input type="file" name="overlayfile[]" class="src" onchange="preview(this)">
<img src="img/someimage.jpg" class="target"/>
</div>
</form>
<p id="item"></p>
The JavaScript Code which I use to preview the files and to send the XMLHttpRequest:
function preview(input) {
if(input.files && input.files[0]) {
var reader = new FileReader();
if(input.files[0].type == "image/jpeg" || input.files[0].type == "image/gif") {
reader.onload = function(e) {
input.nextElementSibling.src = e.target.result;
}
reader.readAsDataURL(input.files[0]);
} else if (input.files[0].type == "application/pdf") {
reader.onload = function(e) {
var targeturl = e.target.result;
getURL(targeturl);
}
}
}
}
function getURL (item) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if(request.readyState == 4 && request.status == 200) {
document.getElementById("item").innerHTML = request.responseText;
}
}
request.open("GET", "preview.php?url="+item, true);
request.send();
}
(I did not write the function to convert the pdf-file into an image yet, currently I'm simply trying to pass the value of target as the content of my paragraph. I'm doing this because I would like to do it step by step so I do not miss any mistakes that would be very annoying later).
When I declare a variable in my getURL() function, it works just fine and the paragraph displays the value of the variable, however, when I call the function from within reader.onload nothing happens.
Does anyone know why that is and how I can fix that?
If you need anymore code/ further explanations please let me know and thank you very much in advance for your help.

Perhaps you don't need to upload them to the server. You could just create a iframe and show it.
Also, you don't need the filereader, creating a objectURL instead. It is faster and better. base64 is ~3 times larger and takes time to (de)compile.
function preview(input) {
const URL = window.URL || window.webkitURL
Array.from(input.files).forEach(file => {
if (file.type.startsWith('image')) {
input.nextElementSibling.src = URL.createObjectURL(file)
} else if (file.type == 'application/pdf') {
let iframe = document.createElement('iframe')
iframe.src = URL.createObjectURL(file)
iframe.onload = function() {
document.getElementById("item").appendChild(iframe)
}
iframe.onerror = function(){
// failed to load pdf
}
}
})
}
if the iframe don't work for you due to something... POST the file to preview.php instead of sending it as base64 using xhr.send(file)

Related

How to get a blob from Javascript with Blazor

I'm using SixLabors.ImageSharp to crop images with Javascript JQuery and all is working rigth but when i need to get the image cropped i don't know how can i get the image without refresh the page and without do a POST.
I'm using Tutexchange tutorial and they obtain the image by a POST method (and i dont wanna do it like that) i think about run a method and with Blazor get the file encoded base64 but when i do it i cant get the reader.result because is inside the onloadend event.
How can i get a image blob without a POST method and without save the image in a folder to read with Blazor?
I tried passing the onloadend reader.result with a method and await with a bucle while to return it when is done but value never is different than null and i tested if the cropper work with a console.log() and all is right with it:
function InitializeCroppie(div_width, div_height) {
basic = $('#main-cropper').croppie
({
enableExif: true,
url: '/images/ChooseImage.png',
viewport: { width: div_width, height: div_height },
boundary: { width: div_width, height: div_height },
showZoomer: false,
format: 'png' //'jpeg'|'png'|'webp'
});
//Reading the contents of the specified Blob or File
function readFile(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#main-cropper').croppie('bind', {
url: e.target.result
});
}
reader.readAsDataURL(input.files[0]);
}
}
// Change Event to Read file content from File input
$('#select').on('change', function () { readFile(this); });
}
var returnThisValue;
async function GetImageCroped() {
returnThisValue = null;
var blob = await basic.croppie('result', 'blob');
var reader = new FileReader();
if (blob != null) {
reader.readAsDataURL(blob);
reader.onloadend = function () {
SetValue(reader.result);
console.log(reader.result);
}
}
while (returnThisValue == null) {
//Waiting...
}
return returnThisValue;
}
function SetValue(value) {
returnThisValue = value;
}
function GetImageValue() {
return returnThisValue;
}
If is impossible without a POST method how can i receive it without reload the page.
EDIT:
I'm doing some test to know if is possible that js let blazor know when put the image in the localStorage to get it synchronic way with Blazor.
AFTER TEST: I tried to get the image with Blazored.LocalStorage and i cannot Chrome has a problem with "big data".
You can add a JavaScript listener event. It will trigger another event when updating the localstorage. It should be loaded when the page is initialized.
var orignalSetItem = localStorage.setItem;
localStorage.setItem = function (key, newValue) {
var setItemEvent = new Event("setItemEvent");
setItemEvent.key = key;
setItemEvent.newValue = newValue;
window.dispatchEvent(setItemEvent);
orignalSetItem.apply(this, arguments);
};
window.addEventListener("setItemEvent", function (e) {
if (e.key == 'image') {
var _this = localStorage.getItem("image")
if (_this != e.newValue) {
console.log(e.newValue)
//call method which sends this blob
} else {
console.log('key->');
console.log(e.newValue)
}
}
});
Trigger method.
$('#btnupload').on('click', function ()
{
basic.croppie('result', 'blob').then(function (blob)
{
localStorage.setItem('image', blob)
//...
});
});

Converting data:image base64 to blob in this code

I'd like to change the URLs from data:image base64 to blob. This is the original code that produces the base64 urls:
<script>
$(window).load(function(){
function readURL() {
var $input = $(this);
var $newinput = $(this).parent().parent().parent().find('.portimg ');
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
reset($newinput.next('.delbtn'), true);
$newinput.attr('src', e.target.result).show();
$newinput.after('<div class="delbtn delete_upload" title="Remove"><span class="bbb-icon bbb-i-remove2"></span></div>');
$("form").on('click', '.delbtn', function (e) {
reset($(this));
$("form").find('#rright-<?php echo $i;?>').hide();
});
}
reader.readAsDataURL(this.files[0]);
}
}
$(".file").change(readURL);
function reset(elm, prserveFileName) {
if (elm && elm.length > 0) {
var $input = elm;
$input.prev('.portimg').attr('src', '').hide();
if (!prserveFileName) {
$($input).parent().parent().parent().find('input.file ').val("");
//input.fileUpload and input#uploadre both need to empty values for particular div
}
elm.remove();
}
}
});
</script>
What I want is to call Object.createObjectURL(this.files[0]) to get the object URL, and use that as the src of your img; (just don't even bother with the FileReader).
Something like this?
function readURL() {
var file = this.files[0]
var reader = new FileReader();
var base64string = getBase64(file);
reader.onload = function () {
reset($newinput.next('.delbtn'), true);
$newinput.attr('src', e.target.result).show();
$newinput.after('<div class="delbtn delete_upload" title="Remove"><span class="bbb-icon bbb-i-remove2"></span></div>');
var blob = dataURItoBlob(base64string);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
I'm not sure if this will work and due to the vagaries of Stack Snippets, can't demonstrate its viability here on Stack Overflow, but theoretically, you should be able to use URL.createObjectURL to create the appropriate URL for your image, without going through the whole base 64 rigmarole.
var $newinput = $(this).parent().parent().parent().find('.portimg ');
if (this.files && this.files[0]) {
$newinput.attr('src', URL.createObjectURL(this.files[0]));
// if the above doesn't work, you could try to create a new Blob
var fileBlob = new Blob(this.files[0], { type: "image/png" })
// Substitute "image/png" with whatever image type it is
$newinput.attr('src', URL.createObjectURL(fileBlob));
That should render the appropriate URL for the image's source.
Note that it is best practice to revoke the object URL when you are done with it. I'm not sure that's necessary in this case, since presumably you want to show the image until the page is closed. However, if the user can upload a new image, do something like:
if ($newinput.attr('src').indexOf('blob') > -1) {
URL.revokeObjectURL($newinput.attr('src'));
}
Add that before setting the new source and you shouldn't need to worry about memory leaks (from this use of createObjectURL anyway...).
For more information on Blob URLs, see this answer by a now-anonymous user to What is a blob URL and why it is used?

Unable to preview image after converting attachment to Base64 using Trix

I'm using Trix, and for uploading attachments our backend developer tells me that I should convert the attachment to base64 and save the base64 data to the database instead of uploading the binary file.
I wrote this code for implementing it, and the output of the input field(HTML) is working as expected, but the image preview doesn't show in the editor.
$(function() {
$(document).on('trix-attachment-add', function(event) {
var attachment = event.originalEvent.attachment;
// Convert data URLs to File objects.
if(attachment.file) {
return uploadAttachment(attachment);
} else {
console.error('Could not upload attachment.');
console.error(attachment);
attachment.remove();
}
});
});
function uploadAttachment(attachment) {
var reader = new FileReader();
console.log(attachment)
// Set the reader to insert images when they are loaded.
reader.onload = function (e) {
var result = e.target.result;
var attrs = {
src : result,
url: result,
href : ''
};
attachment.setAttributes(attrs)
attachment.setUploadProgress(100);
}
// Read image as base64.
reader.readAsDataURL(attachment.file);
}
I don't know what causes this problem.
Try replacing
$(document).on('trix-attachment-add', function(event) {
with
document.addEventListener("trix-attachment-add", function(event) {
This could be event listeners being cached thus firing multiple times. The first load of image works, it could be the next loads that make this look busted.
Could also be Turbolinks issue so wrap your code with this instead:
$(document).on('turbolinks:load', function() {
I've managed to solve the issue by setting the fileObjectURL property as shown below
attachment.attachment.fileObjectURL = result;
Complete code for latest version (works with Symfony 6 easy admin bundle):
(function() {
function asBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
document.addEventListener("trix-file-accept", function(event) {
event.preventDefault();
if (event.file) {
asBase64(event.file).then(function(data) {
let image = document.createElement('img');
image.src = data;
let tmp = document.createElement('div');
tmp.appendChild(image);
let editor = document.querySelector('trix-editor');
editor.editor.insertHTML(tmp.innerHTML);
}).catch(e => console.log(e));
}
}); })();

How can I store a blob in a form and send it to the server when the clicks on submit without Ajax?

This is my function:
FileCropped.prototype.change = function () {
var obj = $(this).data("plugin.file-cropped");
var files = obj.$element[0].files;
var file;
var URL = window.URL || window.webkitURL;
var blobURL;
if (files && files.length) {
file = files[0];
console.log("I have files");
if (/^image\/\w+$/.test(file.type)) {
blobURL = URL.createObjectURL(file);
obj.$element.val('');
obj.$hidden[0].value = blobURL;
//URL.revokeObjectURL(blobURL);
} else {
window.alert('Please choose an image file.');
}
} else
{
console.log("No files?");
}
}
I am trying right now to attach the blob to an existing form input but it does not work. With the chrome debugger I see the method works fine and follow the expected path, but at the time of submit the server gets nothing.
Any hint?
Edit: of course the function has no value right now. I could just use the normal file input. The goal is to be able to manipulate the blob before attaching it to the form.
You can use FileReader to read the file as data URL
var fileReader = new FileReader();
fileReader.addEventListener('load', function () {
blobURL = fileReader.result;
obj.$hidden[0].value = blobURL;
});
fileReader.readAsDataURL(file);

how to detect if a URL points to a SWF

Is there a way (preferrably using JavaScript) to determine whether a URL is to a SWF or a JPG?
The obvious answer is to sniff the filename for ".jpg" or ".swf" but I'm dealing with banners that are dynamically decided by the server and usually have a lot of parameters and generally don't include an extension.
so i'm wondering if I could load the file first and then read it somehow to determine whether it's SWF or JPG, and then place it, because the JavaScript code I'd need to display a JPG vs a SWF is very different.
Thanks!
You could use javascript to detect if it is a image by creating a dynamic img-tag.
function isImage(url, callback) {
var img = document.createElement('img');
img.onload = function() {
callback(url);
}
img.src = url;
}
And then calling it with:
isImage('http://animals.nationalgeographic.com/staticfiles/NGS/Shared/StaticFiles/animals/images/primary/bald-eagle-head.jpg', function(url) { alert(url + ' is a image'); });
Update
This version will always execute the callback with a boolean value.
function isImage(url) {
var img = document.createElement('img');
img.onload = function() {
isImageCallback(url, true);
}
img.onerror = function() {
isImageCallback(url, false);
}
img.src = url;
}
function isImageCallback(url, result) {
if (result)
alert(url + ' is an image');
else
alert(url + ' is not an image');
}
Put your logic in the isImageCallback function.
I would extend Sijin's answer by saying:
An HTTP HEAD request to the url can be used to examine the resource's mime-type. You
won't need to download the rest of the file that way.
Completely untested, basicly just an idea:
function isImage(url)
{
var http = getHTTPObject();
http.onreadystatechange = function ()
{
if (http.readyState == 4)
{
var contentType = http.getResponseHeader("Content Type");
if (contentType == "image/gif" || contentType == "image/jpeg")
return true;
else
return false;
}
}
http.open("HEAD",url,true);
http.send(null);
}
function getHTTPObject()
{
if (window.XMLHttpRequest)
{
return new XMLHttpRequest();
}
else
{
if (window.ActiveXObject)
{
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
return false;
}
I am not sure the of the exact setup you have, but can you use the HTTP response and check the mime-type to determine image vs flash?
If the URL doesn't have an extension then there is no way to tell without requesting the file from the server.

Categories

Resources