Reputation: 27713
I have a situation where I need to store file information (a File
object) in a variable, but I'm required to JSON.stringify
it first. This doesn't work on File
objects:
> JSON.stringify(new File([], null))
« "{}"
So what I need to do is, take a file that a user uploaded, convert it to a plain object, store it in string format, and then later be able to pull that string, JSON.parse
it, and convert it back into a File
. Converting it into a plain object is simple enough, but getting it back to a File
object is something I've been unsuccessful at finding any way to accomplish.
Added clarification:
So let's say I've copied the properties of my File
into a plain object:
{
"lastModified": 1443077616000,
"lastModifiedDate": "2015-09-24T06:53:36.000Z",
"name": "Action Items.zip",
"size": 3619135,
"type": "application/zip"
}
I just need to convert this back into a File
object.
Upvotes: 3
Views: 6065
Reputation: 2503
base on this comment (How to convert a plain JavaScript object to a File object?) you just need a string
Use the FileReader API to transform the File into a DataURL
var fileReader = new FileReader();
// onload needed since Google Chrome doesn't support addEventListener for FileReader
fileReader.onload = function(evt) {
// Read out file contents as a Data URL
var result = evt.target.result;
// do whatever with RESULT
// JSON or whatever
};
// Load blob (File inherits from BLOB) as Data URL
fileReader.readAsDataURL(blob);
Use this function https://github.com/ebidel/filer.js/blob/master/src/filer.js#L131-L158 to transform the DataURL back to a File.
/**
* Creates and returns a blob from a data URL (either base64 encoded or not).
*
* @param {string} dataURL The data URL to convert.
* @return {Blob} A blob representing the array buffer data.
*/
dataURLToBlob: function(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = decodeURIComponent(parts[1]);
return new Blob([raw], {
type: contentType
});
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {
type: contentType
});
}
(you can replace Blob by File everywhere, since File inherits from Blob)
HERE an example of how I can take a file that was dropped by a user, convert it into a base64 string, save it (no localStorage because sandboxing) and, later, take back the string and convert it back to the file that was sent by the user. Isn't it what you want ?
var drop = document.getElementById('drop'),
transform = document.getElementById('transform');
drop.addEventListener('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
var originalFile = null;
drop.addEventListener('drop', function (e) {
e.preventDefault();
e.stopPropagation();
originalFile = event.dataTransfer.files[0];
console.log('originalFile', originalFile);
fileToURLData(originalFile);
}, false);
var storage = {};
function fileToURLData(file) {
var fileReader = new FileReader();
// onload needed since Google Chrome doesn't support addEventListener for FileReader
fileReader.onload = function(evt) {
// Read out file contents as a Data URL
var result = evt.target.result;
/*localStorage.setItem('file-data', JSON.stringify({
dataURL: result,
name: file.name,
lastModified: file.lastModified,
lastModifiedDate: file.lastModifiedDate,
type: "image/jpeg"
}));*/
storage = JSON.stringify({
dataURL: result,
name: file.name,
lastModified: file.lastModified,
lastModifiedDate: file.lastModifiedDate,
type: "image/jpeg"
})
console.log('File dataURL',result);
// do whatever with RESULT
// JSON or whatever
transform.className = 'show';
drop.className = 'hide';
};
// Load blob (File inherits from BLOB) as Data URL
fileReader.readAsDataURL(file);
}
function dataURLToFile(dataURL, name, data) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var raw = decodeURIComponent(parts[1]);
return new File([raw], name, data);
}
var parts = dataURL.split(BASE64_MARKER);
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new File([uInt8Array], name, data);
}
transform.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var fileData= JSON.parse(storage); //localStorage.getItem('file-data')),
dataURL = fileData['dataURL'],
name = fileData.name;
delete fileData.dataURL;
delete fileData.name;
console.log(dataURL, name, fileData);
var file = dataURLToFile(dataURL, name, fileData);
console.log('convertedFile', file);
console.log('originalFile', originalFile);
console.log('originalFile === convertedFile ? ', originalFile === file);
console.log('different files, same values');
alert('DATAURL HAS BEEN CONVERTED BACK TO FILE - SEE CONSOLE');
}, false);
html, html {
width: 100%;
height: 100%;
min-height: 500px;
text-align: center;
font-size: 50px;
}
#drop {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
color: #fff;
}
#drop.hide {
display: none;
}
#transform {
display: none;
}
#transform.show {
display: block;
}
<div id='drop'>DROP FILE HERE</div>
<button id='transform'>Transform back to file</div>
Upvotes: 2