Closed jimmywarting closed 6 years ago
URL.createObjectURL(fakeBlob)
will return a data: URI and fakeBlob.data
will contain the raw data you used to construct it.
fr.readAsArrayBuffer(fakeBlob); // require typed array polyfill (not by you, but from the author)
There is no point in making a typed array polyfill if it doesn't have any performance improvements. You might as well just use a normal array if you want to support IE9.
When would you ever use FileReader on blobs that you already have the creation data for? FileReader is more for blobs you didn't create (File
objects (instanceof Blob
) from file selectors).
I'll consider adding a FileReader, but just not at this moment.
I didn't mean that you had to create a polyfill for arrayBuffer, since they already exist. You could just simply throw an error that "arrayBuffer isn't supported, if you like you could import this typed array library/polyfill to support readAsArrayBuffer..." In that way they can include what's nessecsary to save bytes.
one reason i could think of would be to download chunks of data and merge it into one blob. After that you could read it as text/dataURI/ArrayBuffer of your convenient choice. (a good way do convert the blob/data to another format). maybe you save it in some storage and decide you want to read the text later.
Another reason would be consistency.
If you where to download a big file (say 800 mb) then the browser would start to hang if you load that in to the memory. however there is a way around it if you decided to download chunks file = new Blob([file, chunk])
I manage to load gigabytes of data (movies) from ajax into chrome.
With that you could maybe do a hex editor thats only shows what would be able to fit into the screen
That last thing was a good reason... Download a image from ajax -> wrap it into a blob -> and read only the necessary metadata (size, camera, angle)
var fr = new FileReader();
fr.onload = function(){
// Read metadata info
};
fr.readAsArrayBuffer(fakeBlob.slice(0,1024));
The FileReader could be async. Useful for large blobs where converting lots of data could lead to long running warning dialog in IE (setTimeout to avoid it)
Download a image from ajax -> wrap it into a blob -> and read only the necessary metadata (size, camera, angle)
You can simplify this process using half as much memory: Download a image from ajax -> read only the necessary metadata (size, camera, angle)
Blobs are an unnecessary step that would duplicate the data. Even if you slice the blob, there is still memory and/or disk cache backing the entire blob. It's better to slice the string data (or arraybuffer data if you set request.responseType = "arraybuffer"
) you already have from the xhr.
There are probably other legitimate reasons you might want to use FileReader on fake blobs, so I will get to implementing this eventually. Just understand that this isn't very high priority for me.
I ended up helping github/fetch polyfill and they do more or less depend on Blob & FileReader now when they are starting to use ReadableStream as there underlying source. so they need to convert a stream to a blob and a blob to arrayBuffer or Text
I started to develop a polyfill for the FileReader but wanted to use a other kind of underlying source (a buffer, Uint8Array) since it's more convenient for handling binaries
So i ended up with something like this that I later want to transpile
const wm = new WeakMap
const concatTypedArrays = arrays => {
let totalLength = 0
for (let arr of arrays) {
totalLength += arr.byteLength
}
let result = new Uint8Array(totalLength)
let offset = 0
for (let arr of arrays) {
result.set(arr, offset)
offset += arr.byteLength
}
return result
}
/**
* EventEmitter
*
* [description]
*/
class EventEmitter {
constructor() {
var delegate = document.createDocumentFragment()
;['addEventListener', 'dispatchEvent', 'removeEventListener'].forEach(f =>
this[f] = (...xs) => delegate[f](...xs)
)
}
}
/**
* URL
*
* [description]
*/
class URL {
constructor(url, base) {
if (!url)
throw new TypeError('Invalid argument');
let doc = document.implementation.createHTMLDocument('')
if (base) {
let baseElement = doc.createElement('base')
baseElement.href = base
doc.head.appendChild(baseElement)
}
let anchorElement = doc.createElement('a')
anchorElement.href = url
doc.body.appendChild(anchorElement)
if (anchorElement.protocol === ':' || !/:/.test(anchorElement.href))
throw new TypeError('Invalid URL');
wm.set(this, anchorElement)
}
static createObjectURL(blob) {
if (!(blob instanceof Blob))
throw new TypeError("Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided")
let buffer = wm.get(blob).slice()
let string = new TextDecoder().decode(buffer)
return `data:${blob.type};base64,` + btoa(string)
}
static revokeObjectURL() {}
toString() {
return this.href
}
[Symbol.toStringTag]() {
return 'URL'
}
}
;['href','protocol','username','password','origin','URLSearchParams',
'host','hostname','port','pathname','search','hash'].forEach(name => {
Object.defineProperty(URL.prototype, name, {
get: function() {
return wm.get(this)[name]
},
set: function(value) {
return wm.get(this)[name] = value
}
})
})
/**
* Blob
*
* [description]
*/
class Blob {
constructor(chunks = [], opts = {}) {
if (!Array.isArray(chunks))
throw new TypeError('The 1st argument is not an array')
// convert all chunks to typed arrays
chunks = chunks.map(chunk =>
chunk instanceof Blob ? wm(blob) :
ArrayBuffer.isView(chunk) ? chunk :
chunk instanceof ArrayBuffer ? new Uint8Array(chunk) :
new TextEncoder().encode(chunk += '')
)
let buffer = concatTypedArrays(chunks)
wm.set(this, buffer)
this.size = buffer.byteLength
this.type = opts.type || ''
this.isClosed = false
}
close() {
this.isClosed = true
this.size = 0
wm.delete(this)
}
slice(start = 0, end, type = '') {
let buffer = wm.get(this)
let slice = buffer.slice(start, end || buffer.length)
return new Blob([slice], type)
}
toString() {
return '[object Blob]'
}
[Symbol.toStringTag]() {
return 'Blob'
}
}
/**
* File
*
* [description]
*/
class File extends Blob {
constructor(chunks, filename, opts = {}) {
super(chunks, opts)
this.name = filename + ''
this.lastModified = Date.now()
this.lastModifiedDate = new Date(this.lastModified)
}
toString() {
return '[object File]'
}
get [Symbol.toStringTag]() {
return 'File'
}
}
class Event extends CustomEvent {
constructor(fr, ...args) {
super(...args)
Object.defineProperties(this, {
srcElement: {enumerable: true, value: fr},
target: {enumerable: true, value: fr}
})
}
}
function emit(fr, result) {
setTimeout(() => {
fr.readyState = 2
fr.result = result
for (let type of ['loadstart', 'progress', 'load', 'loadend']) {
let event = new Event(fr, type)
let listener = fr['on' + type]
fr.dispatchEvent(event)
if(listener)
listener.call(fr, event)
}
})
}
/**
* FileReader
*
* [description]
*/
class FileReader extends EventEmitter {
constructor() {
super()
this.readyState = 0
this.result =
this.error =
this.onabort =
this.onerror =
this.onload =
this.onloadend =
this.onloadstart =
this.onprogress = null
}
abort() {}
readAsArrayBuffer(blob) {
emit(this, wm.get(blob).slice().buffer)
}
readAsDataURL(blob) {
let buffer = wm.get(blob).slice()
let string = new TextDecoder().decode(buffer)
emit(this, `data:${blob.type};base64,` + btoa(string))
}
readAsText(blob) {
let buffer = wm.get(blob).slice()
emit(this, new TextDecoder().decode(buffer))
}
toString() {
return '[object FileReader]'
}
[Symbol.toStringTag]() {
return 'FileReader'
}
}
FileReader.EMPTY = 0
FileReader.LOADING = 1
FileReader.DONE = 2
/* // test
var fr = new FileReader
fr.readAsText(new File(['abc'], 'filename.txt'))
fr.onload = console.log
*/
export {FileReader, URL, Blob, File}
Do you feel like this lib needs a face lift? It has a bit more dependencies like TypedArrays, WeakMap, Symbols, TextEncoder, TextDecoder & CustomEvent
@jimmywarting Uncaught TypeError: Failed to construct 'CustomEvent': 1 argument required, but only 0 present.
@guest271314 fixed
@jimmywarting Is it possible to convert a Blob
or File
object to an ArrayBuffer
without using FileReader
? How to create an ArrayBuffer and data URI from Blob and File objects without FileReader?
A way to read the blob data would be grate, whatever its underlying data is (raw, fakeblob, real blob, arraybuffer, whatever) especially for IE9.
I think its mandatory that you can read a blob if you can construct one.