文件(File)实现技术
重要通知
类目简介
基本概况
上传文件
<button type="button">上传文件</button>
<script type="text/javascript">
const button = document.querySelector("button");
button.addEventListener("click", uploadAction, false);
async function uploadAction() {
const fileEl = document.createElement("input");
if (!fileEl) return;
fileEl.setAttribute("type", "file");
fileEl.setAttribute("name", "input-file");
fileEl.setAttribute("multiple", "multiple");
fileEl.setAttribute("accept", "image/gif,image/jpeg,image/png");
fileEl.setAttribute("accept", ".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document");
// accept不传,默认所有文件
// 任何音频文件
fileEl.setAttribute("accept", "audio/*");
// 任何视频文件
fileEl.setAttribute("accept", "video/*");
// 任何图片文件
fileEl.setAttribute("accept", "image/*");
fileEl.click();
const getFileData = (fileTarget) => {
return new Promise((resolve) => {
fileTarget.onchange = async function (e) {
resolve(e.target.files[0]);
};
});
};
const fileData = await getFileData(fileEl);
console.info("fileData: ", fileData);
const getReaderData = (fileData) => {
const reader = new FileReader();
reader.readAsDataURL(fileData); // 二进制
return new Promise((resolve) => {
reader.onload = function () {
resolve(reader.result);
};
});
};
const readerData = await getReaderData(fileData);
console.info("readerData: ", readerData);
}
</script>
File对象
File:https://developer.mozilla.org/zh-CN/docs/Web/API/File
File对象数据结构
{
lastModified: 1713116761687
lastModifiedDate: Mon Apr 15 2024 01:46:01 GMT+0800 (中国标准时间) {}
name: "BB1lA8Hk.png"
size: 524776
type: "image/png"
webkitRelativePath: ""
}
File 对象相关属性
File 对象是来自用户在一个
<input>
元素上选择文件后返回的 FileList 对象 File 接口没有定义任何方法, 从 Blob 接口继承了以下方法, 也继承了 Blob 接口的属性
FileList
File
{
lastModified __(__ 返回当前 File 对象所引用文件最后修改时间,自 UNIX 时间起始值(1970年1月1日 00:00:00 UTC)以来的毫秒数
lastModifiedDate __(__ 返回当前 File 对象所引用文件最后修改时间的 Date 对象。
name __(__ 返回当前 File 对象所引用文件的名字
size __(__ 返回文件的大小
type __(__ 返回文件的 多用途互联网邮件扩展类型(MIME Type)
webkitRelativePath __(__ 返回 File 相关的 path 或 URL。
}
// 结构语法
const file = new File(
bits, // 一个包含ArrayBuffer,ArrayBufferView,Blob,或者 DOMString 对象的 Array — 或者任何这些对象的组合。这是 UTF-8 编码的文件内容。
name, // USVString,表示文件名称,或者文件路径。
options // 选项对象,包含文件的可选属性。type: DOMString,表示将要放到文件中的内容的 MIME 类型。默认值为 "" ,见WEB/AJAX/[Content-Type媒体类型与MIME 类型].md。lastModified: 数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为 Date.now()。
);
File 对象相关方法
Blob.slice
Blob.slice(
start: // 这个参数代表 Blob 里的下标,表示第一个会被会被拷贝进新的 Blob 的字节的起始位置。
end: // 这个参数代表的是 Blob 的一个下标,这个下标-1的对应的字节将会是被拷贝进新的Blob 的最后一个字节。
contentType: // 给新的 Blob 赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。
);
const path = URL.createObjectURL(blob);
URL.revokeObjectURL(path);
FileReader
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
FileReader
FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
FileReader属性
const reader = new FileReader();
reader.readyState
reader.result
// 异步
const getDataText = (content) => {
const reader = new FileReader();
reader.readAsText(content, 'utf-8');
return new Promise((resolve) => {
reader.onload = function() {
resolve(reader.result);
}
});
}
const dataText = await getDataText(textBlob);
FileReader事件
const reader = new FileReader();
//解析文件开始
reader.onloadstart = function(e) {
console.log(e);
}
//解析文件进度
reader.onprogress = function(e) {
console.log(e);
}
//解析文件中断
reader.onabort = function(e) {
console.log(e);
}
//解析文件错误
reader.onerror = function(e) {
console.log(e);
}
//解析文件完成
reader.onload = function(e) {
const result = e.target.result || this.result;
console.log(e);
img.src = result;
//这样就获取到了文件的Base64字符串
//var base64 = getBase64Image(image);
//Base64字符串转二进制
//var file = dataURLtoBlob(base64);
//上传
var blob = new Blob([result], { type: 'image/jpg' });
var fd = new FormData();
fd.append('file', blob);
//提交到服务器, 见ajax
}
//解析文件结束
reader.onloadend = function(e) {
console.log(e);
}
FileReader接口
const file = file.files[0];
const reader = new FileReader();
// 注意:需要在reader.onload中读取,因为内容才能保障解析完成
//读取为文本 readAsText(File|Blob [, encoding]) encoding:asni, utf-8, gbk2312, gb2312, binary
// reader.readAsText(file, 'gb2312');
//将文件读取为 二进制码 readAsBinaryString(File|Blob), 从 2012 年 7 月 12 日起,该方法已从 W3C 工作草案废除。
reader.readAsBinaryString(file);
//将文件读取为 DataURL readAsDataURL(File|Blob)
reader.readAsDataURL(file);
//将文件读取为 缓冲区 readAsArrayBuffer(File|Blob)
reader.readAsArrayBuffer(file);
FileReaderSync
FileReaderSync接口允许以同步的方式读取File或Blob对象中的内容,该接口只在workers里可用,因为在主线程里进行同步I/O操作可能会阻塞用户界面。
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReaderSync
基本类型 MIME-type
# -----------------------------------------------------------------------------------
文件后缀 MIME-type
# -----------------------------------------------------------------------------------
*.3gpp audio/3gpp, video/3gpp
*.ac3 audio/ac3
*.asf allpication/vnd.ms-asf
*.au audio/basic
*.css text/css
*.csv text/csv
*.doc application/msword
*.dot application/msword
*.dtd application/xml-dtd
*.dwg image/vnd.dwg
*.dxf image/vnd.dxf
*.gif image/gif
*.htm text/html
*.html text/html
*.jp2 image/jp2
*.jpe image/jpeg
*.jpeg image/jpeg
*.jpg image/jpeg
*.js text/javascript, application/javascript
*.json application/json
*.mp2 audio/mpeg, video/mpeg
*.mp3 audio/mpeg
*.mp4 audio/mp4, video/mp4
*.mpeg video/mpeg
*.mpg video/mpeg
*.mpp application/vnd.ms-project
*.ogg application/ogg, audio/ogg
*.pdf application/pdf
*.png image/png
*.pot application/vnd.ms-powerpoint
*.pps application/vnd.ms-powerpoint
*.ppt application/vnd.ms-powerpoint
*.rtf application/rtf, text/rtf
*.svf image/vnd.svf
*.tif image/tiff
*.tiff image/tiff
*.txt text/plain
*.wdb application/vnd.ms-works
*.wps application/vnd.ms-works
*.xhtml application/xhtml+xml
*.xlc application/vnd.ms-excel
*.xlm application/vnd.ms-excel
*.xls application/vnd.ms-excel
*.xlt application/vnd.ms-excel
*.xlw application/vnd.ms-excel
*.xml text/xml, application/xml
*.zip application/zip
*.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
# -----------------------------------------------------------------------------------
支持的文件类型
支持 doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx,xlam, xla 等 Office 办公文档 支持 wps, dps, et, ett, wpt 等国产 WPS Office 办公文档 支持 odt, ods, ots, odp, otp, six, ott, fodt, fods 等OpenOffice、LibreOffice 办公文档 支持 vsd, vsdx 等 Visio 流程图文件 支持 wmf, emf 等 Windows 系统图像文件 支持 psd 等 Photoshop 软件模型文件 支持 pdf ,ofd, rtf 等文档 支持 xmind 软件模型文件 支持 bpmn 工作流文件 支持 eml 邮件文件 支持 epub 图书文档 支持 obj, 3ds, stl, ply, gltf, glb, off, 3dm, fbx, dae, wrl, 3mf, ifc, brep, step, iges, fcstd, bim 等 3D 模型文件 支持 dwg, dxf 等 CAD 模型文件 支持 txt, xml(渲染), md(渲染), java, php, py, js, css 等所有纯文本 支持 zip, rar, jar, tar, gzip, 7z 等压缩包 支持 jpg, jpeg, png, gif, bmp, ico, jfif, webp 等图片预览(翻转,缩放,镜像) 支持 tif, tiff 图信息模型文件 支持 tga 图像格式文件 支持 svg 矢量图像格式文件 支持 mp3,wav,mp4,flv 等音视频格式文件 支持 avi,mov,rm,webm,ts,rm,mkv,mpeg,ogg,mpg,rmvb,wmv,3gp,ts,swf 等视频格式转码预览
二进制文件(.bin)
二进制文件,格式binary的缩写,例如main.bin。
数据结构之间转换
Blob转File对象
this.modal.data = URL.createObjectURL(data);
this.modal.visual = false;
// 转换函数
const file = new window.File([data], this.file.name)
let formData = new FormData();
formData.append("content", file);
URL转Base64
URL转Blob
Base64转Blob
Blob转Base64
网络图像文件转Base64
function getBase64Image(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
var dataURL = canvas.toDataURL("image/" + ext);
return dataURL;
};
Base64字符串转二进制
见腾讯云COS上传示例。
function dataURItoBlobUtil(base64: string) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
};
Base64字符串转二进制
dataURItoBlobUtil(dataURI) {
// base64 解码
let byteString = window.atob(dataURI.split(',')[1])
let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
let ab = new ArrayBuffer(byteString.length)
let ia = new Uint8Array(ab)
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ab], {type: mimeString})
}
Blob | 使用 Blob 创建一个指向类型化数组的URL
后端返回blob数据:服务端返回的为资源的二进制数据 前端处理: 前端接收到二进制数据后,使用 URL.createObjectURL(blobData) 方法将服务端返回的二进制数据转换为 blob 的 url 资源挂载到相应的资源对象。
var typedArray = GetTheTypedArraySomehow();
var blob = new Blob([typedArray.buffer], {type: 'application/octet-stream'}); // 传入一个合适的 MIME 类型
var url = URL.createObjectURL(blob);
利用canvas.toDataURL的API转化成base64
function base64ToBlob ({b64data = '', contentType = '', sliceSize = 512} = {}) {
return new Promise((resolve, reject) => {
// 使用 atob() 方法将数据解码
let byteCharacters = atob(b64data);
let byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
let slice = byteCharacters.slice(offset, offset + sliceSize);
let byteNumbers = [];
for (let i = 0; i < slice.length; i++) {
byteNumbers.push(slice.charCodeAt(i));
}
// 8 位无符号整数值的类型化数组。内容将初始化为 0。
// 如果无法分配请求数目的字节,则将引发异常。
byteArrays.push(new Uint8Array(byteNumbers));
}
let result = new Blob(byteArrays, {
type: contentType
})
result = Object.assign(result,{
// 这里一定要处理一下 URL.createObjectURL
preview: URL.createObjectURL(result),
name: `XXX.png`
});
resolve(result)
})
}
字符串与Blob转换
// 字符串转换为Blob
const text = "你好,我是字符串";
const textBlob = new Blob([text],{type: 'text/plain'});
console.info(textBlob);
// Blob转换为字符串
const getDataText = (content) => {
const reader = new FileReader();
reader.readAsText(content, 'utf-8');
return new Promise((resolve) => {
reader.onload = function() {
resolve(reader.result);
}
});
}
const dataText = await getDataText(textBlob);
File对象与Blob对象转换
document.getElementById('uploadFile').onclick = function() {
const file = document.createElement('input');
file.setAttribute('type', 'file');
// file.setAttribute('accept', 'video/mp4,video/webm,video/ogg');
file.click();
const getFileData = (file) => {
return new Promise((resolve) => {
file.onchange = async function(e) {
resolve(e.target.files[0]);
}
});
}
const fileData = await getFileData(file);
console.info('File对象', fileData);
// File转换为Blob
const fileBlob = new Blob([fileData], {type: fileData.type});
console.info(fileBlob);
// Blob转换为File
const fileData = new File([fileBlob], file.name, {type: file.type});
console.info(fileData);
// 上传文件
const formData = new FormData();
formData.append('file', fileData);
}
Base64编码
数据结构
- 图片
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAwAAAAGlCAYAAABNz5H6AAAACXBIWXMAAA7EAAAOxAGVKw4bAAD//0lEQVR4Aey96ZMk23Hll91dvb0VwBDEQpg4w23MJJNJJpP0Rf//R0lfRiMNiY0gsRHkYIiH13t16/yO3xPhERmRmVVd/fAw0u2uuH59d79L7JFX3//v/7d3n3z09PDx06eHq/v3D/cO7w737t07vH331vC7t9SHw713wuvf2+u3alV5K9pb8b958+ZwEP+Dq4eiXx/+9b/8l8O/fvHFQSKHg3RSAF3uyYb+Hj16dPj4k08PTx49cf.....此处省略大量字符串.....AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- 音频
data:audio/mpeg;base64,SUQzAgAAAAAPdlRDTQAACwH//i6WSU5/YgAAVFQxAAAdAf/+ZGvGiZGYCWdzUUYANQDzl1BOIAAyADEAAABUVDIAABMB//5GADUA85dQTiAAMgAxAAAAVEVOAAAhAFByb1RyYW5zY29kZXJUb29sIChBcHBsZSBNUDMgdjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.....此处省略大量字符串.....AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
Base64的编码与解码:https://developer.mozilla.org/zh-CN/docs/Glossary/Base64#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
简介与核心思想
Base64就是一种基于64个可打印字符来表示二进制数据的方法。
解码和编码 base64 字符串
如果传入字符串不是有效的 base64 字符串,比如其长度不是 4 的倍数,则抛出DOMException。
// 编码
let encodedData = window.btoa("Hello, world");
// 解码
let decodedData = window.atob(encodedData);
Blob
注意
File、FileList、FileReader、FileReaderSync继承了Blob, 所以Blob的属性与方法也可以在这些对象中使用。
简介与核心思想
Blob() 构造函数返回一个新的 Blob 对象。 blob的内容由参数数组中给出的值的串联组成。
语法
new Blob(
array, // array 是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8
options: { // options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性, {}
type: '', // type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。
endings: 'transparent' // endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: "native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持blob中保存的结束符不变
}
);
Blob属性
Blob.size:Blob 对象中所包含数据的大小(字节)。 Blob.type:一个字符串,表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。
Blob方法
Blob.slice([start[, end[, contentType]]]):返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。 Blob.stream():返回一个能读取blob内容的 ReadableStream。 Blob.text():返回一个promise且包含blob所有内容的UTF-8格式的 USVString。 Blob.arrayBuffer():返回一个promise且包含blob所有内容的二进制格式的 ArrayBuffer
从Blob中读取数据
// 方法一:一种从Blob中读取内容的方法是使用 FileReader。
var reader = new FileReader();
reader.addEventListener("loadend", function() {
// reader.result 包含被转化为类型数组 typed array 的 blob
});
reader.readAsArrayBuffer(blob);
// 方法二:另一种读取Blob中内容的方式是使用Response对象。
var text = await (new Response(blob)).text();
代码示例
var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'});
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // 一个包含DOMString的数组
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // 得到 blob
let typedArrayToURL = (typedArray, mimeType) => {
return URL.createObjectURL(new Blob([typedArray.buffer], {type: mimeType}))
// 产生一个类似 blob:d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的URL字符串
}
let bytes = new Uint8Array(59);
for (let i=0; i<59; i++) {
bytes[i] = 32+i;
}
let url = typedArrayToURL(bytes, "text/plain");
let link = document.createElement("a");
link.href = url;
link.innerText = "Open the array URL";
document.body.appendChild(link);
<input type="file" id="single" onchange="singleFile()">
/**
* 大文件分片上传技术
*
*/
//兼容性支持
function blobSlice(blob) {
let blobSlice = blob;
if (blob.mozSlice) { //Firefox 12及更早版本
blobSlice = blob.mozSlice;
}
if (blob.webkitSlice) { //Safari旧版本
blobSlice = blob.webkitSlice;
}
//其他
blobSlice = blob.slice;
//分片技术
return blobSlice;
}
//分片上传
function Burst() {
//
}
let file = document.getElementsByTagName('input')[0];
function singleFile() {
let content = file.files[0];
let size = content.size; //name、size、type、lastModified
console.log('文件分片前大小: ' + size);
let scale = 100 * 1024;
//小于scale,则不分片
if (size > scale) {
//
}
let multiple = Math.ceil(size / scale);
console.log('分片数量:' + multiple);
let remainder = size % scale;
//没有余数,全是倍数
if (remainder == 0) {
//
}
let integer = scale * (multiple - 1);
let total = integer + remainder;
console.log('分片合并后大小:' + total);
//整合分片数据结构
let sliceData = [];
for (let i = 0; i < multiple; i++) {
(function(i) {
let slice = {};
slice['type'] = content.type;
slice['name'] = content.name;
slice['lastModified'] = content.lastModified;
slice['total'] = content.size;
slice['id'] = i;
slice['prev'] = i*scale;
slice['next'] = (i+1)*scale;
slice['blob'] = content.slice(i*scale, (i+1)*scale);
slice['size'] = scale;
//兼容分片不足整数部分
if (i == multiple - 1) {
let last = i*scale + remainder;
slice['next'] = last;
slice['blob'] = content.slice(i*scale, last);
slice['size'] = remainder;
}
sliceData.push(slice);
})(i);
}
recurrence(sliceData);
//decollate(sliceData);
}
//分割上传-递归等待分片上传成功
function recurrence(sliceData) {
sliceData = sliceData || [];
//递归推送, 成功继续推送
let count = 0;
function pushBlob(i) {
//推送完成
if (count == sliceData.length) {
return ;
}
let dx = sliceData[i];
//封装数据
let formData = new FormData();
formData.append("content", dx.blob);
//拼接片段标志参数
let query = "";
for (let i in dx) {
if (i != 'blob') {
query += '&' + i + '=' + dx[i];
}
}
query = query.replace(/^&/, '?');
//开始推送
ajax({
base: 'http://127.0.0.1:7901/',
path: 'decollate' + query,
method: 'post',
type: 'upload',
timeout: 1500000,
size: dx.size,
data: formData,
complete() {},
success(res) {
//继续推送下一流
count += 1;
pushBlob(count);
},
fail(err) {
//推送失败,重复推送
pushBlob(count);
}
});
}
pushBlob(count);
}
//分割上传-演示
function decollate(sliceData) {
sliceData = sliceData || [];
sliceData.forEach((dx)=> {
let formData = new FormData();
formData.append("content", dx.blob);
//拼接片段标志参数
let query = "";
for (let i in dx) {
if (i != 'blob') {
query += '&' + i + '=' + dx[i];
}
}
query = query.replace(/^&/, '?');
//推送服务器
ajax({
base: 'http://127.0.0.1:7901/',
path: 'decollate' + query,
method: 'post',
type: 'upload',
timeout: 1500000,
size: dx.size,
data: formData,
complete() {},
success(res) {
//console.log(res);
},
fail(err) {
//console.log(err);
}
});
});
}