JavaScript: Making a subclass of Blob - javascript

I am experimenting to see if I can customize a Blob that I will pass to a FileReader. The goal of the sample code is to be able to programmatically
generate data on the fly for the FileReader to consume. However, it looks like I am not subclassing correctly as I get a TypeError when I try to use my custom class.
Here is my sample code:
// Example of reading a real blob
var realBlob = new Blob(['foo', 'bar']);
var reader1 = new FileReader();
reader1.onload = function(event){
console.log(JSON.stringify(reader1.result));
};
reader1.readAsText(realBlob);
// Writes "foobar"
// (non-working) streaming blob
function StreamingBlob() {}
StreamingBlob.prototype = new Blob;
StreamingBlob.prototype.constructor = StreamingBlob;
var bInst = new StreamingBlob();
bInst.slice = function (start, end) {
str = '';
while (start++ < end) {
str += 'A';
}
return new Blob([str]);
}
// Instance of says it's a Blob
console.log(bInst instanceof StreamingBlob);
console.log(bInst instanceof Blob);
console.log(bInst);
console.log(bInst.slice(1,5));
var reader2 = new FileReader();
reader2.onload = function(event){
console.log(JSON.stringify(reader2.result));
};
reader2.readAsText(bInst);
// Error!
When I run this I see:
true
true
StreamingBlob {slice: function, type: "", size: 0, constructor: function}
Blob {type: "", size: 4, slice: function}
Uncaught TypeError: Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
"foobar"
I'm confused, because the instanceof check claims that my object is a Blob, but the readAsText method generates an error that claims otherwise.
Is there a better way to do this?

Related

JavaScript object to file object

I'm working on adding images to page, do something with collection of added images (preview etc) and finally I want them save. Everything is cool until the files object is used to show or save the photo.
var input = document.getElementById('files');
var files = input.files;
as it is an array of objects read only - it is impossible to manipulate it freely. For working with that array friendly I maped it like that:
var addedFiles = added(files);
function added(from) {
return $.map(from, function (i) {
var x = { lastModified: i.lastModified, lastModifiedDate: i.lastModifiedDate, name: i.name, size: i.size, type: i.type, webkitRelativePath: i.webkitRelativePath }
return x;
});
}
... then do something with those files - and I want to preview, and then save - but for example during preview I get an error:
Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
function readImage(file) {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
image.addEventListener("load", function () {
preview.innerHTML += drawHtml(this, file);
window.URL.revokeObjectURL(image.src); //blob version
});
image.src = reader.result; //file version
image.src = window.URL.createObjectURL(file) //blob version
});
reader.readAsDataURL(file); // here fire the error
}
When I pass for testing originally file obj to above code every thing is working.
Question:
How to create custom obj (in my case array of obj) that can be parse to file obj
P.S. In project I'm using jquery and javascript
Rather than mapping the File objects to new, incompatible objects, you could instead wrap them with the additional things you need, but then use the underlying original files when reading them:
const fileSelections = Array.prototype.map.call(input.files, file => ({
// This will let you get to the underlying file in the wrapper objects
file,
// If you want pass-throughs, you can do stuff like this:
get lastModified() { return file.lastModified },
// And you can add your own properties/methods as you please
});
function readImage(fileSelection) {
// Unwrap the file
const file = fileSelection.file;
const reader = new FileReader();
reader.addEventListener("load", function () {
const image = new Image();
image.addEventListener("load", function () {
preview.innerHTML += drawHtml(this, file);
window.URL.revokeObjectURL(image.src); //blob version
});
image.src = reader.result; //file version
image.src = window.URL.createObjectURL(file) //blob version
});
reader.readAsDataURL(file);
}
correct answer is blob - it's something amazing for me.
//from is the array of obj - files
function added(from) {
var out = [];
for (var i = 0; i < from.length; i++) {
(function (obj) {
var readerBase64 = new FileReader();
var obj = from[i];
readerBase64.addEventListener("load", function () {
var fileBase64 = readerBase64.result;
var row = { name: obj.name, size: obj.size, type: obj.type, base64: fileBase64 }
out.push(row);
});
readerBase64.readAsDataURL(obj);
})(from[i]);
}
return out;
}
'out' is a table of my own objects with base64, so I can create images for preview and 'do something functions' in the end I'm going to use base64 for create files.
here link for question related to my next step - creating img from blob (where I'm using additional lib b64toBlob)

How to convert string to File object in javascript ?

I am trying to convert string to File object in javascript but I get an error.
my code:
var contents = fs.readFileSync('./dmv_file_reader.txt').toString()
var readerfile1 = new File([""], contents);
(i have to use contents as file and not as string)
and my output is :
ReferenceError: File is not defined
at d:\Workspace\DMV\dist\win-ia32-unpacked\resources\app.asar\main.js:67:32
at process._tickCallback (internal/process/next_tick.js:103:7)
any solution some1?
First you have to create blob from Javascript object and that blob object can be passed to File() constructor to create a File Object.Hope this helps.
var contents = fs.readFileSync('./dmv_file_reader.txt').toString()
var blob = new Blob([contents], { type: 'text/plain' });
var file = new File([blob], "foo.txt", {type: "text/plain"});

How to create File object from Blob?

DataTransferItemList.add allows you to override copy operation in javascript. It, however, only accepts File object.
Copy event
The code in my copy event:
var items = (event.clipboardData || event.originalEvent.clipboardData);
var files = items.items || items.files;
if(files) {
var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
files.add(blob);
}
The error in chrome:
Uncaught TypeError: Failed to execute add on DataTransferItemList: parameter 1 is not of type File.
Trying the new File(Blob blob, DOMString name)
In Google Chrome I tried this, according to the current specification:
var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
var file = new File(blob, "image.png");
Problem here is, that Google Chrome doesn't stick to specifications very much.
Uncaught TypeError: Failed to construct File: Illegal constructor
Neither does Firefox in this case:
The method parameter is missing or invalid.
Trying the new File([Mixed blobParts], DOMString name, BlobPropertyBag options)
Solution suggested by #apsillers doesn't work too. This is non stadard method used (but useless) in both Firefox and Chrome.
Binary data
I tried to avoid blob, but the file constructor failed anyway:
//Canvas to binary
var data = atob( //atob (array to binary) converts base64 string to binary string
_this.editor.selection.getSelectedImage() //Canvas
.toDataURL("image/png") //Base64 URI
.split(',')[1] //Base64 code
);
var file = new File([data], "image.png", {type:"image/png"}); //ERROR
You can try that in console:
Chrome <38:
Chrome >=38:
Firefox:
Blob
Passing Blob is probably correct and works in Firefox:
var file = new File([new Blob()], "image.png", {type:"image/png"});
Firefox:
Chrome <38:
Chrome >=38:
Q: So how can I make File from Blob?
Note: I added more screenshots after #apsillers reminded me to update Google Chrome.
The File constructor (as well as the Blob constructor) takes an array of parts. A part doesn't have to be a DOMString. It can also be a Blob, File, or a typed array. You can easily build a File out of a Blob like this:
new File([blob], "filename")
This was the complete syntax which I had to use to convert a blob into a file, which I later had to save to a folder using my server.
var file = new File([blob], "my_image.png",{type:"image/png", lastModified:new Date().getTime()})
this works with me, from canvas to File [or Blob], with filename!
var dataUrl = canvas.toDataURL('image/jpeg');
var bytes = dataUrl.split(',')[0].indexOf('base64') >= 0 ?
atob(dataUrl.split(',')[1]) :
(<any>window).unescape(dataUrl.split(',')[1]);
var mime = dataUrl.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++) {
ia[i] = bytes.charCodeAt(i);
}
var newImageFileFromCanvas = new File([ia], 'fileName.jpg', { type: mime });
Or if you want a blob
var blob = new Blob([ia], { type: mime });

Use Blob on firefox add-on

Been trying to get the following code to work in firefox add-on:
var oMyForm = new FormData();
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // number 123456 is immediately converted to string "123456"
// HTML file input user's choice...
oMyForm.append("userfile", fileInputElement.files[0]);
// JavaScript file-like object...
var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
var oBlob = new Blob([oFileBody], { type: "text/xml"});
oMyForm.append("webmasterfile", oBlob);
var oReq = new XMLHttpRequest();
oReq.open("POST", "http://foo.com/submitform.php");
oReq.send(oMyForm);
from https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects?redirectlocale=en-US&redirectslug=Web%2FAPI%2FFormData%2FUsing_FormData_Objects
So I know I have to use XPCOM, but I can't find the equivalent. I found this so far:
var oMyForm = Cc["#mozilla.org/files/formdata;1"].createInstance(Ci.nsIDOMFormData);
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // number 123456 is immediately converted to string "123456"
// JavaScript file-like object...
var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
var oBlob = Cc["#mozilla.org/files/file;1"].createInstance(Ci.nsIDOMFile, [oFileBody], { type: "text/xml"});
oMyForm.append("webmasterfile", oBlob);
var oReq = Cc["#mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
oReq.open("POST", "http://localhost:3000");
oReq.send(oMyForm);
Essentially the problem is var oBlob = Cc["#mozilla.org/files/file;1"].createInstance(Ci.nsIDOMFile, [oFileBody], { type: "text/xml"}); because "#mozilla.org/files/file;1" or Ci.nsIDOMFile is incorrect. Note that nsIDOMFile is inherits from nsIDOMBlob.
Anyone know what to do?
Thanks a bunch.
Let's cheat a little to answer this:
JS Code Modules actually have Blob and File, while SDK modules do not :(
Cu.import() will return the full global of a code module, incl. Blob.
Knowing that, we can just get a valid Blob by importing a known module, such as Services.jsm
Complete, tested example, based on your code:
const {Cc, Ci, Cu} = require("chrome");
// This is the cheat ;)
const {Blob, File} = Cu.import("resource://gre/modules/Services.jsm", {});
var oMyForm = Cc["#mozilla.org/files/formdata;1"].createInstance(Ci.nsIDOMFormData);
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // number 123456 is immediately converted to string "123456"
// JavaScript file-like object...
var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
var oBlob = Blob([oFileBody], { type: "text/xml"});
oMyForm.append("webmasterfile", oBlob, "myfile.html");
var oReq = Cc["#mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
oReq.open("POST", "http://example.org/");
oReq.send(oMyForm);

Chrome Extension Blob data issue

I am trying to create a blob from a canvas image from within a Chrome extension, however I am getting an error "Uncaught TypeError: object is not a function" when trying to create a Blob using any method!
var blob = new Blob();
var blob = new Blob(['body { color: red; }'], {type: 'text/css'});
are two examples that fail with the above error. I am actually trying to convert a DataURL to a blob so the code I am using (which also fails) is...
function dataURItoBlob(dataURI) {
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1])
} else {
byteString = decodeURI(dataURI.split(',')[1])
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i)
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
I am assuming that Chrome will not support new blobs??
The issue was that the call to create a Blob was being done from a JS file, the correct place was the background JavaScript file. By moving the method to create the blob into the background file I as able to use it.

Categories

Resources