I have this code to save an excel using Blob
//Stream of data as res
var dataView = new DataView(res);
var blob = new Blob([dataView], {type: 'application/vnd.ms-excel'});
But in IE only the third line throw Invalid State Error even though in the document, it is fully supported
It seems that the issue pertains to IE. Uint8Array can be used in the constructor instead.
To convert a DataView to a equivalent Uint8Array:
var u8arr = new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength);
Write a function to replace all DataView objects in the array passed to new Blob. Or see the polyfill here.
Related
I'm passing a few kB of data (a generated PNG file) from a Unity3D WebGL context to javascript so that the user can download the PNG file without leaving the WebGL context. Unity uses emscripten and embeds the js as jslib. It's the fist time I've looked at emscripten or used pointers in js, having trouble finding the basics in the emscripten docs.
It's working, but I think it is a poor implementation, here's the code:
mergeInto(LibraryManager.library, {
JSDownload: function(filenamePointer, dataPointer, dataLength) {
filename = Pointer_stringify(filenamePointer);
var data = new Uint8Array(dataLength);
for (var i = 0; i < dataLength; i++) {
data[i]=HEAPU8[dataPointer+i];
}
var blob = new Blob([data], {type: 'application/octet-stream'});
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else{
var elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
});
What bothers me is stepping through the data like that, since I already have the address and the length I want to instantiate the 'data' array at the known address, like I would with * and & in C, rather than copying it byte by byte, or if I have to copy it, at least do that in one hit rather than a loop. I think my biggest issue is not knowing where to look for the documentation. I've found more from looking at random projects on GitHub than here: https://emscripten.org/docs/api_reference/preamble.js.html
Any help would be appreciated, thanks.
So you don't like this part?
var data = new Uint8Array(dataLength);
for (var i = 0; i < dataLength; i++) {
data[i]=HEAPU8[dataPointer+i];
}
var blob = new Blob([data], {type: 'application/octet-stream'});
You can make it one-liner:
var blob = new Blob([HEAPU8.subarray(dataPointer, dataPointer + dataLength)], {type: 'application/octet-stream'});
// or this
var blob = new Blob([new Uint8Array(HEAPU8.buffer, dataPointer, dataLength)], {type: 'application/octet-stream'});
Both of them should be much faster then your original code, and both of them should have exactly the same performance. It's because they create a new Blob directly from HEAPU8 without creating duplicated array like your original code.
HEAPU8 is a Uint8Array, one of TypedArray family. One really important thing about TypedArray is that it is actually not buffer/data but it's rather a "view" of the underlying ArrayBuffer (it's HEAPU8.buffer) object which holds the actual data. See ArrayBufferView.
So HEAPU8 provides an interface for HEAPU8.buffer ArrayBuffer object, specifically WebAssembly.Memory.buffer in Emscripten, to look like an uint8_t array. Emscripten also provides HEAPU16, HEAPU32, HEAPF32, and etc but they have the same ArrayBuffer with different views.
What .subarray(start, end) and new Uint8Array(buffer, offset, size) do is to create a new "view" of the ArrayBuffer object with the specified range, not to copy the buffer. So you will have the minimal performance penalty.
I have a Blob Object, which is an image and I am trying to convert into a file object, But it shows errors in MS edge version 41. I am using formdata in 1st two attempts for the same
Attempt 1
fd.set('file', blobObj, fileName);
return (fd.get('file'));
This resulted in an error
object doesn't support this property or method 'set'
Attempt 2
I replaced set with append and then I got this
object doesn't support this property or method 'get'
Attempt 3
I replaced formdata entirely with a new logic which looked like this
let fileObject = new File([u8arr], fileName, { type: mime });
and I got an error saying
object doesn't support this action
Is there any other method that can be used? Can I directly use blob as a file?
AFAIK, Your third approach seems to be working ,
Try once by hard-coding the mime type to "image/jpeg" / "image/png" and include the date modeified and then verify once
var fileInstance = new File([blob], "FileName",{type:"image/jpeg", lastModified:new Date()})
If you are displaying it in javascript you should use something like this:
var URL = window.URL || window.webkitURL;
var url_instance = URL.createObjectURL(blob);
var image_source = new Image();
image_source.src = url_instance;
document.body.appendChild(image_source);
A File object is a specific kind of a Blob, it's just missing the two properties: lastModifiedDate and name(file name property).
So, you could convert the blob object to file object using the following code:
var blobtoFile = function blobToFile(theBlob, fileName) {
//A Blob() is almost a File() - it's just missing the two properties below which we will add
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
return theBlob;
}
var file = blobtoFile(blob, "test.png");
More detail information about using the above code, please check this sample.
Besides, please check the FormData Method Browser compatibility, from it we can see most of the methods support Microsoft Edge 44+(EdgeHTML 18+, more detail, please check this article).
So, if you want to use FormData set or get method, please try to upgrade the Windows version(Microsoft Edge is part of the operating system and can't be updated separately. It receives updates through Windows Update, like the rest of the operating system.). Otherwise, you could use a JavaScript Object to store the blob or file object.
Detail updated steps as below: Select Start > Settings > Update & Security > Windows Update , then select Check for updates and install any available updates.
Properties of files received from an <input type="file"> are read-only.
For example, the following attempt to re-write file.name would either fail silently or throw TypeError: Cannot assign to read only property 'name' of object '#<File>'.
<input onchange="onchange" type="file">
onchange = (event) => {
const file = event.target.files[0];
file.name = 'foo';
}
Attempting to create a copy via Object.assign({}, file) fails (creates an empty object).
So how does one clone a File object?
My solution lay in the File constructor:
https://developer.mozilla.org/en-US/docs/Web/API/File#Implementation_notes
Which itself is an extension of Blob:
https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
let file = event.target.files[0];
if (this.props.distro) {
const name = 'new-name-here' + // Concat with file extension.
file.name.substring(file.name.lastIndexOf('.'));
// Instantiate copy of file, giving it new name.
file = new File([file], name, { type: file.type });
}
Note the first argument to File() must be an array, not simply the original file.
You can use FormData.prototype.append(), which also converts a Blob to a File object.
let file = event.target.files[0];
let data = new FormData();
data.append("file", file, file.name);
let _file = data.get("file");
A more cross browser solution
The accepted answer works for me too in modern browsers, but unfortunately it does not work in IE11, since IE11 does not support the File constructor.
However, IE11 does support the Blob constructor so it can be used as an alternative.
For example:
var newFile = new Blob([originalFile], {type: originalFile.type});
newFile.name = 'copy-of-'+originalFile.name;
newFile.lastModifiedDate = originalFile.lastModifiedDate;
Source: MSDN - How to create a file instannce using HTML 5 file API?
There's a File object in JavaScript. I want to instantiate one for testing purposes.
I have tried new File(), but I get an "Illegal constructor" error.
Is it possible to create a File object ?
File Object reference : https://developer.mozilla.org/en/DOM/File
According to the W3C File API specification, the File constructor requires 2 (or 3) parameters.
So to create a empty file do:
var f = new File([""], "filename");
The first argument is the data provided as an array of lines of text;
The second argument is the filename ;
The third argument looks like:
var f = new File([""], "filename.txt", {type: "text/plain", lastModified: date})
It works in FireFox, Chrome and Opera, but not in Safari or IE/Edge.
Now you can!
var parts = [
new Blob(['you construct a file...'], {type: 'text/plain'}),
' Same way as you do with blob',
new Uint16Array([33])
];
// Construct a file
var file = new File(parts, 'sample.txt', {
lastModified: new Date(0), // optional - default = now
type: "overide/mimetype" // optional - default = ''
});
var fr = new FileReader();
fr.onload = function(evt){
document.body.innerHTML = evt.target.result + "<br>Download " + file.name + "<br>type: "+file.type+"<br>last modified: "+ file.lastModifiedDate
}
fr.readAsText(file);
Update
BlobBuilder has been obsoleted see how you go using it, if you're using it for testing purposes.
Otherwise apply the below with migration strategies of going to Blob, such as the answers to this question.
Use a Blob instead
As an alternative there is a Blob that you can use in place of File as it is what File interface derives from as per W3C spec:
interface File : Blob {
readonly attribute DOMString name;
readonly attribute Date lastModifiedDate;
};
The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system.
Create the Blob
Using the BlobBuilder like this on an existing JavaScript method that takes a File to upload via XMLHttpRequest and supplying a Blob to it works fine like this:
var BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder;
var bb = new BlobBuilder();
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://jsfiddle.net/img/logo.png', true);
xhr.responseType = 'arraybuffer';
bb.append(this.response); // Note: not xhr.responseText
//at this point you have the equivalent of: new File()
var blob = bb.getBlob('image/png');
/* more setup code */
xhr.send(blob);
Extended example
The rest of the sample is up on jsFiddle in a more complete fashion but will not successfully upload as I can't expose the upload logic in a long term fashion.
Now it's possible and supported by all major browsers: https://developer.mozilla.org/en-US/docs/Web/API/File/File
var file = new File(["foo"], "foo.txt", {
type: "text/plain",
});
The idea ...To create a File object (api) in javaScript for images already present in the DOM :
<img src="../img/Products/fijRKjhudDjiokDhg1524164151.jpg">
var file = new File(['fijRKjhudDjiokDhg1524164151'],
'../img/Products/fijRKjhudDjiokDhg1524164151.jpg',
{type:'image/jpg'});
// created object file
console.log(file);
Don't do that ! ... (but I did it anyway)
-> the console give a result similar as an Object File :
File(0) {name: "fijRKjokDhgfsKtG1527053050.jpg", lastModified: 1527053530715, lastModifiedDate: Wed May 23 2018 07:32:10 GMT+0200 (Paris, Madrid (heure d’été)), webkitRelativePath: "", size: 0, …}
lastModified:1527053530715
lastModifiedDate:Wed May 23 2018 07:32:10 GMT+0200 (Paris, Madrid (heure d’été)) {}
name:"fijRKjokDhgfsKtG1527053050.jpg"
size:0
type:"image/jpg"
webkitRelativePath:""__proto__:File
But the size of the object is wrong ...
Why i need to do that ?
For example to retransmit
an image form already uploaded, during a product update, along with additional images added during the update
Because this is javascript and dynamic you could define your own class that matches the File interface and use that instead.
I had to do just that with dropzone.js because I wanted to simulate a file upload and it works on File objects.
I've been using Blob of HTML5 to consolidate a bunch of data including files and strings. Since the files and strings to be sent are not pre-specified in program, and I need to pack all data in a JS file and send them immediately, so I use Array of javascript to collect available data, then make this array as a parameter of Blob constructor. It works fine in Chrome and Firefox, but throws a javascript error when using IE11.
Unhandled exception at line 161, column 9 in ##$%.js
0x800a139e - JavaScript Runtime Error: InvalidStateError
My code is as follows:
var blobPackage_array = [];
if(userType != null)
blobPackage_array.push(userType);
if(userInfo != null)
blobPackage_array.push(userInfo);
for (var i = 0; i < fileList.length; i++) {
blobPackage_array.push(fileList[i]);
}
var blobPackage = new Blob(blobPackage_array); //throw javascript runtime error
I previously suspected that IE doesn't support Blob, so I tested this:
var blobPackage = new Blob(["test", fileList[0]]);
It worked fine, no error. My last guess is that IE doesn't recognise blobPackage_array as a valid parameter of Blob constructor. But Blob doesn't have a append method, meanwhile I can not know how many files that need to be appended, which means I can not construct a Blob once and for all.
Anyone ever encounter this? anything I can use to bypass this? I'd appreciate any suggestion.
Update! For some reason, I can not use FormData instead, It has to be blob...
anybody can help me on this?
Update again! Thanks to your kind reply, there are some progress. I checked MSDN, Blob's constructor should be like this: var blobObject = new Blob([new Uint8Array(array)], { type: 'image/png' });. I tried to construct a Uint8Array with blobPackage_array by this var uint8array = new Uint8Array(blobPackage_array);. I find that data is lost while this transformation. But in fact, var blobPackage = new Blob([uint8array]); can work, without errors. Thus I just need to fix the transformation problem.
I figured this out. I'm such an idiot..IE do not recognize my original blobPackage_array as a valid parameter because of those variables I append:
if(userType != null)
blobPackage_array.push(userType);
I just need to validate userType by this:
if(userType != null)
blobPackage_array.push(new String(userType));
So, don't bother to transform all data to UInt8Array type...