ffxsam
ffxsam

Reputation: 27713

How to convert a plain JavaScript object to a File object?

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

Answers (1)

dievardump
dievardump

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

Related Questions