I have a file upload that saves images as .png on the server and the link in a mysql database. To show thumbnails of the image before uploading I have a function that convertes the file list object to a preview pic. Now I want the user to edit the file selection later. For that I need to load the images from the server back as thumbnails. I think the best way to do this is to convert the file path stored in the database to a file object and apply this object to the function that creates the thumbnails that I don't need to rewrite this function.
So my question is how can I convert my stored image links to a file list object?
Edit:
upload:
user selection -> file object -> base 64 -> blob -> display blob -> (maybe) edit -> upload selection as base 64 to server -> base 64 to .png -> save pic -> save link
later edit selection by user(how to do?):
saved link -> file object -> base 64 -> blob -> display blob -> (maybe) edit -> ...
saved link -> file object How to do? Possible? Better way?
I hope it's now clearer to understand.
If someone has an idea how to do this or a better way please answer.
(I know that you should show Code when asking a question but I don't think that it is necesarry to upload the whole upload function here)
You process is all frowned, so I may not answer correctly your question, but I'm pretty sure this is an X-Y problem.
Javascript File objects inherit from the Blob object.
From MDN :
A File object is a specific kind of a Blob, and can be used in any context that a Blob can.
Never convert a File to a base64 dataURL, if it's to convert it back to a Blob ; this makes no sense and only pollute the browser's memory.
To display a Blob (or a File) in the browser, from media elements, or iframes, use the URL.createObjectURL(blob) method. This will return a blobURI, that will be available only for the life time of the initiating page, and only for the user's browsers. In case of user uploaded Files, the file is not even copied to memory, and the uri returned is just a direct pointer to the file on the user's system.
inp.onchange = function(){
var url = URL.createObjectURL(this.files[0]);
var img = new Image();
img.onload = function(){
document.body.appendChild(this);
}
img.onerror = function(){
console.log('probably not a supported image file');
}
img.src = url;
}
<input id="inp" type="file" accept="image/*">
If you need to modify the image, you can do so by drawing the resulting image on a canvas.
Then, instead of exporting your canvas to a dataURI, directly use the toBlob method, which can be polyfilled.
To send you File/Blob on your server, send the File/Blob directly instead of its 30% heavier base64 string representation.
This can be done really easily thanks to the FormData API.
var xhr = new XMLHttpRequest();
xhr.onload = done;
var formData = new FormData();
formData.append('file', theFile, fileName);
xhr.open('POST', yourServer);
xhr.send(formData);
Then you can retrieve it server side just like any File uploaded through the basic Multipart/Form method.
Related
I am using react-easy-crop to allow users to modify their profile pictures after uploading. The problem I am experiencing is that after cropping, the image is returned in the form of a blob url like this: blob:http://localhost:3000/5e44190e-a087-4683-b3a4-dfce4a57ee62 which is unhelpful since it can only be viewed on my local machine.
I have tried converting it to a data url (which I understand can then be shared and viewed across browsers), using FileReader and the readAsDataURL() method like this:
let blob = await fetch(imageToCrop).then((r) => r.blob());
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
let base64data = reader.result;
console.log(base64data);
};
The base64data variable does return what I think I need, however all my attempts to then store this result in my state only return a null value.
Does anyone know what is the best way to handle this?
If you have this line in your code, delete it because it revoke your URL.
window.URL.revokeObjectURL(this.fileUrl);
If you need the base64 output of the cropped image, you can get it after using the canvas to crop it with canvas.toDataURL('image/jpeg'). This base64 string can then be shared to anyone or uploaded to a remote server.
This is basically the commented line in the getCroppedImg() function of this demo: https://codesandbox.io/s/q8q1mnr01w?file=/src/cropImage.js:2362-2562
BTW I guess you could also upload the blob to a remote server and store the image somewhere like AWS S3.
How do you view a blob data that is from a sql database file (.db .db3 and others) and view it on web browser by only using a single html file? The blob data are probably meant to be seen as an image file (jpg, png and others)
Let's say I have a blob data like this:
du�� C�BVwv�q8q7k�1�H�asfdasdfasdf�#s;47sk"as��'7hib-�3$asdffdsfa�a�����U�����P������
And I want to put that single blob data directly (without calling the database file, just using the value of the blob itself) inside a html file so I can directly open it from my browser without installing other software or setting up a local server inside my computer.
I'm sorry if I explain this weirdly, I rarely code, I honestly don't know anything about sql or that server thingamajig, I just want to view the blob file.
You could use Blob. Here I construct a blob and then turn it back into a string that I insert in the document.body.
var array = ['<p>Hello World!</p>'];
var blob = new Blob(array, {type : 'text/html'});
const reader = new FileReader();
reader.addEventListener('loadend', e => {
document.body.innerHTML += e.target.result;
});
reader.readAsText(blob);
And I guess that the Filereader can also read a file if needed.
I want to convert a blob file to a png. I tried this:
var blob = new Blob([ia], {type: 'image/png'});
$scope.farmerRegisterObj.farmerImage = blob;
I want to convert it into file object ..and should be able to append in a Formdata.
A Blob is a File like object, or more exactly, a File object is a Blob with a name property.
You can create a File object from a Blob thanks to the File(blob, name) constructor, but this will be useless in almost every use cases*.
All you can do with a File can be done with a Blob.
For instance, in your case, you can append a Blob directly into a FormData so that it is sent as a file/multipart :
var form = new FormData();
form.append(fieldName, blob, fileName);
* I only used it once, while trying to hack the behavior of browsers default page title when viewing a file like an image in a tab, and it only partially worked only in FF. If someone has real use cases where a File is needed, I'd be glad to know about it.
I have an HTML5/javscript app which uses
<input type="file" accept="image/*;capture=camera" onchange="gotPhoto(this)">
to capture a camera image. Because my app wants to be runnable offline, how do I save the File (https://developer.mozilla.org/en-US/docs/Web/API/File) object in local storage, such that it can be retrieved later for an ajax upload?
I'm grabbing the file object from the using ...
function gotPhoto(element) {
var file = element.files[0];
//I want to save 'file' to local storage here :-(
}
I can Stringify the object and save it, but when I restore it, it is no longer recognised as a File object, and thus can't be used to grab the file content.
I have a feeling it can't be done, but am open to suggestions.
FWIW My workaround is to read the file contents at store time and save the full contents to local storage. This works, but quickly consumes local storage since each file is a 1MB plus photograph.
You cannot serialize file API object.
Not that it helps with the specific problem, but ...
Although I haven't used this, if you look at the article it seems that there are ways (although not supported yet by most browsers) to store the offline image data to some files so as to restore them afterward when the user is online (and not to use localStorage)
Convert it to base64 and then save it.
function gotPhoto(element) {
var file = element.files[0];
var reader = new FileReader()
reader.onload = function(base64) {
localStorage["file"] = base64;
}
reader.readAsDataURL(file);
}
// Saved to localstorage
function getPhoto() {
var base64 = localStorage["file"];
var base64Parts = base64.split(",");
var fileFormat = base64Parts[0].split(";")[1];
var fileContent = base64Parts[1];
var file = new File([fileContent], "file name here", {type: fileFormat});
return file;
}
// Retreived file object
Here is a workaround that I got working with the code below. I'm aware with your edit you talked about localStorage but I wanted to share how I actually implemented that workaround. I like to put the functions on body so that even if the class is added afterwards via AJAX the "change" command will still trigger the event.
See my example here: http://jsfiddle.net/x11joex11/9g8NN/
If you run the JSFiddle example twice you will see it remembers the image.
My approach does use jQuery. This approach also demonstrates the image is actually there to prove it worked.
HTML:
<input class="classhere" type="file" name="logo" id="logo" />
<div class="imagearea"></div>
JS:
$(document).ready(function(){
//You might want to do if check to see if localstorage set for theImage here
var img = new Image();
img.src = localStorage.theImage;
$('.imagearea').html(img);
$("body").on("change",".classhere",function(){
//Equivalent of getElementById
var fileInput = $(this)[0];//returns a HTML DOM object by putting the [0] since it's really an associative array.
var file = fileInput.files[0]; //there is only '1' file since they are not multiple type.
var reader = new FileReader();
reader.onload = function(e) {
// Create a new image.
var img = new Image();
img.src = reader.result;
localStorage.theImage = reader.result; //stores the image to localStorage
$(".imagearea").html(img);
}
reader.readAsDataURL(file);//attempts to read the file in question.
});
});
This approach uses the HTML5 File System API's to read the image and put it into a new javascript img object. The key here is readAsDataURL. If you use chrome inspector you will notice the images are stored in base64 encoding.
The reader is Asynchronous, this is why it uses the callback function onload. So make sure any important code that requires the image is inside the onLoad or else you may get unexpected results.
You could use this lib:
https://github.com/carlo/jquery-base64
then do something similar to this:
//Set file
var baseFile = $.base64.encode(fileObject);
window.localStorage.setItem("file",basefile);
//get file
var outFile = window.localStorage.getItem("file");
an other solution would be using json (I prefer this method)
using: http://code.google.com/p/jquery-json/
//Set file
window.localStorage.setItem("file",$.toJSON(fileobject));
//get file
var outFile = $.evalJSON(window.localStorage.getItem("file"));
I don't think that there is a direct way to Stringify and then deserialize the string object into the object of your interest. But as a work around you can store the image paths in your local storage and load the images by retrieving the URL for the images. Advantages would be, you will never run out of storage space and you can store 1000 times more files there.. Saving an image or any other file as a string in local storage is never a wise decision..
create an object on the global scope
exp: var attmap = new Object();
after you are done with file selection, put your files in attmap variable as below,
attmap[file.name] = attachmentBody;
JSON.stringify(attmap)
Then you can send it to controller via input hidden or etc. and use it after deserializing.
(Map<String, String>)JSON.deserialize(attachments, Map<String,String>.class);
You can create your files with those values in a for loop or etc.
EncodingUtil.base64Decode(CurrentMapValue);
FYI:This solution will also cover multiple file selection
You could do something like this:
// fileObj = new File(); from file input
const buffer = Buffer.from(await new Response(fileObj).arrayBuffer());
const dataUrl = `data:${fileObj.type};base64,${buffer.toString("base64")}`;
localStorage.setItem('dataUrl', dataUrl);
then you can do:
document.getElementById('image').src = localStorage.getItem('dataUrl');
I have the following code to write an image into the filesystem, and read it back for display. Prior to trying out the filesystem API, I loaded the whole base64 image into the src attribute and the image displayed fine. Problem is the images can be large so if you add a few 5MB images, you run out of memory. So I thought I'd just write them to the tmp storage and only pass the URL into the src attribute.
Trouble is, nothing gets displayed.
Initially I thought it might be something wrong with the URL, but then I went into the filesystem directory, found the image it was referring to and physically replaced it with the real binary image and renamed it to the same as the replaced image. This worked fine and the image is displayed correctly, so the URL looks good.
The only conclusion I can come to is that the writing of the image is somehow wrong - particularly the point where the blob is created. I've looked through the blob API and can't see anything that I may have missed, however I'm obviously doing something wrong because it seems to be working for everyone else.
As an aside, I also tried to store the image in IndexedDB and use the createObjectURL to display the image - again, although the URL looks correct, nothing is displayed on the screen. Hence the attempt at the filesystem API. The blob creation is identical in both cases, with the same data.
The source data is a base64 encoded string as I mentioned. Yes, I did also try to store the raw base64 data in the blob (with and without the prefix) and that didn't work either.
Other info - chrome version 28, on linux Ubuntu
//strip the base64 `enter code here`stuff ...
var regex = /^data.+;base64,/;
if (regex.test(imgobj)) { //its base64
imgobj = imgobj.replace(regex,"");
//imgobj = B64.decode(imgobj);
imgobj = window.atob(imgobj);
} else {
console.log("it's already :", typeof imgobj);
}
// store the object into the tmp space
window.requestFileSystem(window.TEMPORARY, 10*1024*1024, function(fs) {
// check if the file already exists
fs.root.getFile(imagename, {create: false}, function(fileEntry) {
console.log("File exists: ", fileEntry);
callback(fileEntry.toURL(), fileEntry.name);
//
}, function (e) { //file doesn't exist
fs.root.getFile(imagename, {create: true}, function (fe) {
console.log("file is: ", fe);
fe.createWriter(function(fw){
fw.onwriteend = function(e) {
console.log("write complete: ", e);
console.log("size of file: ", e.total)
callback(fe.toURL(), fe.name);
};
fw.onerror = function(e) {
console.log("Write failed: ", e.toString());
};
var data = new Blob([imgobj], {type: "image/png"});
fw.write(data);
}, fsErrorHandler);
}, fsErrorHandler);
});
// now create a file
}, fsErrorHandler);
Output from the callback is:
<img class="imgx" src="filesystem:file:///temporary/closed-padlock.png" width="270px" height="270px" id="img1" data-imgname="closed-padlock.png">
I'm at a bit of a standstill unless someone can provide some guidance...
UPDATE
I ran a test to encode and decode the base64 image with both the B64encoder/decoder and atob/btoa -
console.log(imgobj); // this is the original base64 file from the canvas.toDataURL function
/* B64 is broken*/
B64imgobjdecode = B64.decode(imgobj);
B64imgobjencode = B64.encode(B64imgobjdecode);
console.log(B64imgobjencode);
/* atob and btoa decodes and encodes correctly*/
atobimgobj = window.atob(imgobj);
btoaimgobj = window.btoa(atobimgobj);
console.log(btoaimgobj);
The results show that the btoa/atob functions work correctly but the B64 does not - probably because the original encoding didn't use the B64.encode function...
The resulting file in filesystem TEMPORARY, I ran through an online base64 encoder for comparison and the results are totally different. So the question is - while in the filesystem temp storage, is the image supposed to be an exact image, or is it padded with 'something' which only the filesystem API understands? Remember I put the original PNG in the file system directory and the image displayed correctly, which tends to indicate that the meta-data about the image (eg. the filename) is held elsewhere...
Can someone who has a working implementation of this confirm if the images are stored as images in the filesystem, or are padded with additional meta-data?
So to answer my own question - the core problem was in the base64 encoding/decoding - I've since then changed this to use things like ajax and responseTypes like arraybuffer and blob and things have started working.
To answer the last part of the question, this is what I've found - in the filesystem tmp storage, yes the file is supposed to be an exact binary copy - verified this in chrome and phonegap.