Blob對象和File對象
Blob對象是不可變的原始數據, 可以讀取為文本或二進制數據.
File 對象是特殊類型的 Blob,且可以用在任意的 Blob 類型的 context 中。比如說, FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。
new Blob([text1, text2], { type: 'application/octet-stream' });
new File([file], 'rename.mp4'); // 復制文件並臨時重命名
代碼
/**
* 演示使用a標簽生成下載, 命名下載文件名, 數據轉base64
* Data URLs 由四個部分組成:前綴(data:)、指示數據類型的MIME類型、如果非文本則為可選的base64標記、數據本身:
* data:[<mediatype>][;base64],<data>
*/
import { Component } from 'react';
import Button from '@/components/Button';
import Code from '@/components/Code';
class Demo extends Component {
handleClick(event) {
let a = document.createElement('a');
let text = 'ABC你好'; // 要存放的utf-8文本, 無法使用btoa轉base64編碼
switch (event.target.innerText) {
case '使用URI編碼文本后再base64編碼的Data URLs': {
a.href = `data: application/octet-stream; base64,${btoa(encodeURI(text))}`; // 文本中含有漢字而無法使用btoa轉義, 因此使用URI編碼文本
break;
}
case '使用utf-8編碼文本的Data URLs': {
a.href = `data: application/octet-stream; utf-8,${text}`; // 不使用base64, 使用utf-8編碼保存文件
break;
}
/* 在每次調用 createObjectURL() 方法時,都會創建一個新的 URL 對象,即使你已經用相同的對象作為參數創建過。
* 當不再需要這些 URL 對象時,每個對象必須通過調用 URL.revokeObjectURL() 方法來釋放。
*/
case '使用Blob創建ObjectURL本地鏈接': {
let blob = new Blob([text, 123], { type: 'application/octet-stream' });
console.log(blob); // Blob { size: 12, type: "application/octet-stream" }
a.href = URL.createObjectURL(blob);
console.log(a.href); // 一個以"blob:"開頭的URL地址
break;
}
case '使用FileReader將Blob編碼為Data URLs': {
let reader = new FileReader();
reader.addEventListener('loadend', event => {
a.href = event.target.result;
console.log(a.href); // Data URLs
// 這里是異步的, 所以要重復操作
a.download = '文件.end'; // 8位位組八位字節流可以自定義文件后綴
a.click(); // 遺憾的是, 無法感知文件下載完成事件, 從而無法主動釋放Blob對象創建的ObjectURL
});
let blob = new Blob([text], { type: 'application/octet-stream' });
reader.readAsDataURL(blob);
break;
}
}
a.download = '文件.end'; // 8位位組八位字節流可以自定義文件后綴
a.click(); // 遺憾的是, 無法感知文件下載完成事件, 從而無法主動釋放Blob對象創建的ObjectURL
}
render() {
return (
<div>
<Button onClick={_ => this.handleClick(_)}>使用URI編碼文本后再base64編碼的Data URLs</Button>
<Button onClick={_ => this.handleClick(_)}>使用utf-8編碼文本的Data URLs</Button>
<Button onClick={_ => this.handleClick(_)}>使用Blob創建ObjectURL本地鏈接</Button>
<Button onClick={_ => this.handleClick(_)}>使用FileReader將Blob編碼為Data URLs</Button>
</div>
);
}
}
export default (
<div>
<Demo />
<Code>{require('!raw-loader!.').default}</Code>
</div>
);
前端
缺陷:函數參數有限,所以大數據會拋出錯誤RangeError: Maximum call stack size exceeded。
function String2ArrayBuffer(str, callback) {
var b = new Blob([str]);
var f = new FileReader();
f.onload = function (e) {
callback(e.target.result);
}
f.readAsArrayBuffer(b);
}
String2ArrayBuffer('你好abc', function (buf) {
const base64String = window.btoa(String.fromCharCode(...new Uint8Array(buf)));
console.log(base64String);
});
修復:
function String2ArrayBuffer(str, callback) {
var b = new Blob([str]);
var f = new FileReader();
f.onload = function (e) {
callback(e.target.result);
}
f.readAsArrayBuffer(b);
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
String2ArrayBuffer('你好abc', function (buf) {
const base64String = arrayBufferToBase64(buf);
console.log(base64String);
});
Node.js
