Blob&File&FileReader

Blob

The Blob object represents a blob, which is a file-like object of immutable, raw data; they can be read as text or binary data, or converted into a ReadableStream so its methods can be used for processing the data.

Blobs can represent data that isn’t necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user’s system.

Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写。它通常用来读写文件,它的名字是 Binary Large Object (二进制大型对象)的缩写。它与 ArrayBuffer 的区别在于,它用于操作二进制文件,而 ArrayBuffer 用于操作内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Contructor
Blob(array [, options])
# Returns a newly created Blob object which contains a concatenation of all of the data in the array passed into the constructor.

# Instance properties
Blob.prototype.size - Read only
# The size, in bytes(8 bit), of the data contained in the Blob object.

Blob.prototype.type - Read only
# A string indicating the MIME type of the data contained in the Blob. If the type is unknown, this string is empty.

# Instance methods
Blob.prototype.arrayBuffer()
# Returns a promise that resolves with an ArrayBuffer containing the entire contents of the Blob as binary data.

Blob.prototype.slice([start[, end[, contentType]]])
# Returns a new Blob object containing the data in the specified range of bytes of the blob on which it's called.

Blob.prototype.stream()
# Returns a ReadableStream that can be used to read the contents of the Blob.

Blob.prototype.text()
# Returns a promise that resolves with a USVString containing the entire contents of the Blob interpreted as UTF-8 text.

Creating a blob

1
2
const obj = {hello: 'world'};
const blob = new Blob([JSON.stringify(obj, null, 2)], {type : 'application/json'});

Creating a URL representing the contents of a typed array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<p>This example creates a typed array containing the ASCII codes
for the space character through the letter Z, then converts it
to an object URL. A link to open that object URL is created.
Click the link to see the decoded object URL.</p>

/***********************************/

function typedArrayToURL(typedArray, mimeType) {
return URL.createObjectURL(new Blob([typedArray.buffer], {type: mimeType}))
}

const bytes = new Uint8Array(59);

for(let i = 0; i < 59; i++) {
bytes[i] = 32 + i;
}

const url = typedArrayToURL(bytes, 'text/plain');

const link = document.createElement('a');
link.href = url;
link.innerText = 'Open the array URL';

document.body.appendChild(link);

Extracting data from a blob

1
2
3
4
5
const reader = new FileReader();
reader.addEventListener('loadend', () => {
// reader.result contains the contents of blob as a typed array
});
reader.readAsArrayBuffer(blob);
1
2
# Response is an interface of the Fetch API
const text = await (new Response(blob)).text();
1
const text = await blob.text();

File

File objects are generally retrieved from a FileList object returned as a result of a user selecting files using the element, from a drag and drop operation’s DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement.

A File object is a specific kind of a Blob, and can be used in any context that a Blob can. In particular, FileReader, URL.createObjectURL(), createImageBitmap(), and XMLHttpRequest.send()) accept both Blobs and Files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Constructor
File()

# Instance properties
File.prototype.lastModified - Read only
# Returns the last modified time of the file, in millisecond since the UNIX epoch (January 1st, 1970 at Midnight).

File.prototype.lastModifiedDate - Read only
# Returns the last modified Date of the file referenced by the File object.

File.prototype.name - Read only
# Returns the name of the file referenced by the File object.

File.prototype.webkitRelativePath - Read only
# Returns the path the URL of the File is relative to.

# File implements Blob, so it also has the following properties available to it:

File.prototype.size - Read only
# Returns the size of the file in bytes.

File.prototype.type - Read only
# Returns the MIME type of the file.

# Instance methods
# The File interface doesn't define any methods, but inherits methods from the Blob interface:

Blob.prototype.slice([start[, end[, contentType]]])
# Returns a new Blob object containing the data in the specified range of bytes of the source Blob.

Blob.prototype.stream()
# Transforms the File into a ReadableStream that can be used to read the File contents.

Blob.prototype.text()
# Transforms the File into a stream and reads it to completion. It returns a promise that resolves with a USVString (text).

Blob.prototype.arrayBuffer()
# Transforms the File into a stream and reads it to completion. It returns a promise that resolves with an ArrayBuffer.

FileReader & FileReaderSync

The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user’s computer, using File or Blob objects to specify the file or data to read.

Important note: FileReader is used to read file content from the user’s (remote) system in secure ways only. It cannot be used to read a file by pathname from a file system. To read files by pathname in JavaScript, standard Ajax solutions should be used to do server-side file reading, with CORS permission if reading cross-domain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# Constructor
FileReader()

# Properties
FileReader.error - Read only
# A DOMException representing the error that occurred while reading the file.

FileReader.readyState - Read only
# A number indicating the state of the FileReader. This is one of the following:
# EMPTY 0 No data has been loaded yet.
# LOADING 1 Data is currently being loaded.
# DONE 2 The entire read request has been completed.

FileReader.result - Read only
# The file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation.

# Event handlers
FileReader.onabort
# A handler for the abort event. This event is triggered each time the reading operation is aborted.

FileReader.onerror
# A handler for the error event. This event is triggered each time the reading operation encounter an error.

FileReader.onload
# A handler for the load event. This event is triggered each time the reading operation is successfully completed.

FileReader.onloadstart
# A handler for the loadstart event. This event is triggered each time the reading is starting.

FileReader.onloadend
# A handler for the loadend event. This event is triggered each time the reading operation is completed (either in success or failure).

FileReader.onprogress
# A handler for the progress event. This event is triggered while reading a Blob content.

# As FileReader inherits from EventTarget, all those events can also be listened for by using the addEventListener method.

# Methods
FileReader.abort()
# Aborts the read operation. Upon return, the readyState will be DONE.

FileReader.readAsArrayBuffer()
# Starts reading the contents of the specified Blob, once finished, the result attribute contains an ArrayBuffer representing the file's data.

FileReader.readAsBinaryString()
# Starts reading the contents of the specified Blob, once finished, the result attribute contains the raw binary data from the file as a string.

FileReader.readAsDataURL()
# Starts reading the contents of the specified Blob, once finished, the result attribute contains a data: URL representing the file's data.

FileReader.readAsText()
# Starts reading the contents of the specified Blob, once finished, the result attribute contains the contents of the file as a text string. An optional encoding name can be specified.

# Events
# Listen to these events using addEventListener() or by assigning an event listener to the oneventname property of this interface.

abort
# Fired when a read has been aborted, for example because the program called FileReader.abort().
# Also available via the onabort property.

error
# Fired when the read failed due to an error.
# Also available via the onerror property.

load
# Fired when a read has completed successfully.
# Also available via the onload property.

loadend
# Fired when a read has completed, successfully or not.
# Also available via the onloadend property.

loadstart
# Fired when a read has started.
# Also available via the onloadstart property.

progress
# Fired periodically as data is read.
# Also available via the onprogress property.

Using files from web applications

Using the File API, which was added to the DOM in HTML5, it’s now possible for web content to ask the user to select local files and then read the contents of those files. This selection can be done by either using an HTML <input type="file"> element or by drag and drop.

Accessing selected file(s)

1
2
3
4
<input type="file" id="input" multiple>

# Accessing the first selected file using a classical DOM selector:
const selectedFile = document.getElementById('input').files[0];

Accessing selected file(s) on a change event

1
2
3
4
5
const inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
const fileList = this.files; /* now you can work with the file list */
}

Getting information about selected file(s)

1
2
3
4
5
6
7
8
const numFiles = fileList.length;

for (let i = 0, numFiles = fileList.length; i < numFiles; i++) {
const file = fileList[i];
// file.name
// file.size
// file.type
}

Example: Showing file(s) size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>File(s) size</title>
</head>

<body>
<form name="uploadForm">
<div>
<input id="uploadInput" type="file" name="myFiles" multiple>
selected files: <span id="fileNum">0</span>;
total size: <span id="fileSize">0</span>
</div>
<div><input type="submit" value="Send file"></div>
</form>

<script>
function updateSize() {
let nBytes = 0,
oFiles = this.files,
nFiles = oFiles.length;
for (let nFileId = 0; nFileId < nFiles; nFileId++) {
nBytes += oFiles[nFileId].size;
}
let sOutput = nBytes + " bytes";
// optional code for multiples approximation
const aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
for (nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
}
// end of optional code
document.getElementById("fileNum").innerHTML = nFiles;
document.getElementById("fileSize").innerHTML = sOutput;
}

document.getElementById("uploadInput").addEventListener("change", updateSize, false);
</script>
</body>
</html>

Using hidden file input elements using the click() method

You can hide the admittedly ugly file element and present your own interface for opening the file picker and displaying which file or files the user has selected. You can do this by styling the input element with display:none and calling the click() method on the element.

1
2
3
4
5
6
7
8
9
10
11
12
13
<input type="file" id="fileElem" multiple accept="image/*" style="display:none">
<button id="fileSelect">Select some files</button>

/********************************/

const fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem");

fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
}, false);

Using a label element to trigger a hidden file input element

There is no need to add JavaScript code to call fileElem.click(). Also in this case you can style the label element as you wish. You need to provide a visual cue for the focus status of the hidden input field on its label, be it an outline as shown above, or background-color or box-shadow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<input type="file" id="fileElem" multiple accept="image/*" class="visually-hidden">
<label for="fileElem">Select some files</label>

/************************************/

.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}

/* Separate rule for compatibility, :focus-within is required on modern Firefox and Chrome */
input.visually-hidden:focus + label {
outline: thin dotted;
}
input.visually-hidden:focus-within + label {
outline: thin dotted;
}

Selecting files using drag and drop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let dropbox;

dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);

function dragenter(e) {
e.stopPropagation();
e.preventDefault();
}

function dragover(e) {
e.stopPropagation();
e.preventDefault();
}

function drop(e) {
e.stopPropagation();
e.preventDefault();

const dt = e.dataTransfer;
const files = dt.files;

handleFiles(files);
}

Example: Showing thumbnails of user-selected images

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function handleFiles(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];

if (!file.type.startsWith('image/')){ continue }

const img = document.createElement("img");
img.classList.add("obj");
img.file = file;
preview.appendChild(img); // Assuming that "preview" is the div output where the content will be displayed.

const reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
}

Using object URLs

The DOM URL.createObjectURL() and URL.revokeObjectURL() methods let you create simple URL strings that can be used to reference any data that can be referred to using a DOM File object, including local files on the user’s computer.

1
2
3
const objectURL = window.URL.createObjectURL(fileObj);

URL.revokeObjectURL(objectURL);

Example: Using object URLs to display images

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<input type="file" id="fileElem" multiple accept="image/*" style="display:none">
<a href="#" id="fileSelect">Select some files</a>
<div id="fileList">
<p>No files selected!</p>
</div>

/******************************************/

const fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem"),
fileList = document.getElementById("fileList");

fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
}, false);

fileElem.addEventListener("change", handleFiles, false);

function handleFiles() {
if (!this.files.length) {
fileList.innerHTML = "<p>No files selected!</p>";
} else {
fileList.innerHTML = "";
const list = document.createElement("ul");
fileList.appendChild(list);
for (let i = 0; i < this.files.length; i++) {
const li = document.createElement("li");
list.appendChild(li);

const img = document.createElement("img");
img.src = URL.createObjectURL(this.files[i]);
img.height = 60;
img.onload = function() {
URL.revokeObjectURL(this.src);
}
li.appendChild(img);
const info = document.createElement("span");
info.innerHTML = this.files[i].name + ": " + this.files[i].size + " bytes";
li.appendChild(info);
}
}
}

Example: Uploading a user-selected file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function sendFiles() {
const imgs = document.querySelectorAll(".obj");

for (let i = 0; i < imgs.length; i++) {
new FileUpload(imgs[i], imgs[i].file);
}
}

function FileUpload(img, file) {
const reader = new FileReader();
this.ctrl = createThrobber(img);
const xhr = new XMLHttpRequest();
this.xhr = xhr;

const self = this;
this.xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
const percentage = Math.round((e.loaded * 100) / e.total);
self.ctrl.update(percentage);
}
}, false);

xhr.upload.addEventListener("load", function(e){
self.ctrl.update(100);
const canvas = self.ctrl.ctx.canvas;
canvas.parentNode.removeChild(canvas);
}, false);
xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
reader.onload = function(evt) {
xhr.send(evt.target.result);
};
reader.readAsBinaryString(file);
}

Asynchronously handling the file upload process

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
if (isset($_FILES['myFile'])) {
// Example:
move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . $_FILES['myFile']['name']);
exit;
}
?><!DOCTYPE html>
<html>
<head>
<title>dnd binary upload</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="application/javascript">
function sendFile(file) {
const uri = "/index.php";
const xhr = new XMLHttpRequest();
const fd = new FormData();

xhr.open("POST", uri, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText); // handle response.
}
};
fd.append('myFile', file);
// Initiate a multipart/form-data upload
xhr.send(fd);
}

window.onload = function() {
const dropzone = document.getElementById("dropzone");
dropzone.ondragover = dropzone.ondragenter = function(event) {
event.stopPropagation();
event.preventDefault();
}

dropzone.ondrop = function(event) {
event.stopPropagation();
event.preventDefault();

const filesArray = event.dataTransfer.files;
for (let i=0; i<filesArray.length; i++) {
sendFile(filesArray[i]);
}
}
}
</script>
</head>
<body>
<div>
<div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">Drag & drop your file here...</div>
</div>
</body>
</html>

Example: Using object URLs to display PDF

1
2
3
4
5
6
<iframe id="viewer">

const obj_url = URL.createObjectURL(blob);
const iframe = document.getElementById('viewer');
iframe.setAttribute('src', obj_url);
URL.revokeObjectURL(obj_url);

Example: Using object URLs with other file types

1
2
3
4
5
const video = document.getElementById('video');
const obj_url = URL.createObjectURL(blob);
video.src = obj_url;
video.play();
URL.revokeObjectURL(obj_url);

Example: 判断文件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
// 文件开头的四个字节,生成一个 Blob 对象
var slice = file.slice(0, 4);
var reader = new FileReader();
// 读取这四个字节
reader.readAsArrayBuffer(slice);
reader.onload = function (e) {
var buffer = reader.result; // e.target.result
// 将这四个字节的内容,视作一个32位整数
var view = new DataView(buffer);
var magic = view.getUint32(0, false);
// 根据文件的前四个字节,判断它的类型
switch(magic) {
case 0x89504E47: file.verified_type = 'image/png'; break;
case 0x47494638: file.verified_type = 'image/gif'; break;
case 0x25504446: file.verified_type = 'application/pdf'; break;
case 0x504b0304: file.verified_type = 'application/zip'; break;
}
console.log(file.name, file.verified_type);
};
}
Reference
  1. https://developer.mozilla.org/en-US/docs/Web/API/Blob
  2. https://developer.mozilla.org/en-US/docs/Web/API/File
  3. https://developer.mozilla.org/en-US/docs/Web/API/FileReader
  4. https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications