关于下载
1. 前端下载
超链接下载
普通链接
<a href='/xxx/xxx.jpg' download='file.jpg'>下载jpg图片</a>
- 优势
- 简洁方便
- 缺点
使用 blob URL
<a href='blob:http://127.0.0.1:5500/b447bb3d-0a33-4234-8a3d-2a1ce5cdaae2'/>
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);
HTMLCanvasElement.toBlob()
使用 DataURL
<a href='data:image/jpeg;base64,xxxxxxxxxx='></a>
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // 将 Blob 转换为 base64 并调用 onload
reader.onload = function() {
link.href = reader.result; // data url
link.click();
};
HTMLCanvasElement.toDateURL()
iframe 下载
2. 后端下载
设置 header
Content-Disposition、content-type
在常规的 HTTP 应答中,Content-Disposition 响应标头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。
Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
拓展
blob
定义
ArrayBuffer 对象是 ES6 才纳入正式 ECMAScript 规范,是 JavaScript 操作二进制数据的一个接口。Blob 可以认为就是「文件」(所以blob是有文件类型的,即mime type),只不过是脱离具体文件系统的文件(不需要有文件名、文件路径之类的东西)。BLOB是一个大文件,典型的BLOB是一张图片或一个声音文件。
在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型。在 JavaScript 中 Blob 类型的对象表示不可变的类似文件对象的原始数据。
Blob 表示的不一定是 JavaScript 原生格式的数据。Blob并不像 ArrayBuffer 是JS语言内置的,而是Web API,Node.js的API里就没有Blob。例如,File 接口基于 Blob,继承了 blob 的功能并将其扩展以支持用户系统上的文件。
API 简介
developer.mozilla.org/zh-CN/docs/…
Blob (Binary Large Object)对象表示一个不可变、原始数据的类文件对象,也是一个二进制类型的大对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。 Blob 由一个可选的字符串 type(通常是 MIME 类型)和 blobParts 组成。
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
常见的 MIME 类型有:超文本标记语言文本 .html text/html、PNG图像 .png image/png、普通文本 .txt text/plain 等。
const blob = new Blob(['hello blob'], {type: 'text/plain'});
const bufferPromise = await blob.arrayBuffer();
使用场景
- 下载
- 图像处理(canvas)
- 图像预览
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>通过 DATAURL 和 blob 进行图片预览</title>
</head>
<body>
<h1>1. DataUrl 方式</h1>
<input type="file" accept="*/*" onchange="selectImageFromDATAURL(event)"></input>
<img id="output1"></img>
<h1>2. Blob 方式</h1>
<input type="file" accept="*/*" onchange="selectImageFromBLOB(event)"></input>
<img id="output2"></img>
<h1>Canvas</h1>
<canvas id="canvas" width="5" height="5"></canvas>
<img id="output3" />
<script type="text/javascript">
// 1. DataURL 方式
async function selectImageFromDATAURL(event) {
const reader = new FileReader()
reader.onload = () => {
const output = document.getElementById('output1')
output.src = reader.result
}
reader.readAsDataURL(event.target.files[0])
}
// 2. BLOB 方式
async function selectImageFromBLOB(event) {
const output = document.getElementById('output2')
const imgUrl = window.URL.createObjectURL(event.target.files[0])
output.src = imgUrl
output.onload = () => {
window.URL.revokeObjectURL(imgUrl)
}
}
// 3. canvas
</script>
</body>
</html>
- 资源分段上传
const file = new File(["a".repeat(1000000)], "test.txt");
const chunkSize = 40000;
const url = "https://httpbin.org/post";
async function chunkedUpload() {
for (let start = 0; start < file.size; start += chunkSize) {
const chunk = file.slice(start, start + chunkSize + 1);
const fd = new FormData();
fd.append("data", chunk);
await fetch(url, { method: "post", body: fd }).then((res) =>
res.text()
);
}
}
- 图片压缩
const MAX_WIDTH = 800; // 图片最大宽度
function compress(base64, quality, mimeType) {
let canvas = document.createElement("canvas");
let img = document.createElement("img");
img.crossOrigin = "anonymous";
return new Promise((resolve, reject) => {
img.src = base64;
img.onload = () => {
let targetWidth, targetHeight;
if (img.width > MAX_WIDTH) {
targetWidth = MAX_WIDTH;
targetHeight = (img.height * MAX_WIDTH) / img.width;
} else {
targetWidth = img.width;
targetHeight = img.height;
}
canvas.width = targetWidth;
canvas.height = targetHeight;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除画布
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
let imageData = canvas.toDataURL(mimeType, quality / 100);
resolve(imageData);
};
});
}
ArrayBuffer
定义
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 对象是 ES6 才纳入正式 ECMAScript 规范,是 JavaScript 操作二进制数据的一个接口。ArrayBuffer其实就是一块连续内存。你可以将这块内存映射为某种数组(TypedArray)或者是自定义的数据视图(DataView),将来如果JS有了Struct(或TypedObject),有可能可以映射为结构体(Struct)或结构体数组
它是一个字节数组,通常在其他语言中称为“byte array”。你不能直接操作 ArrayBuffer 中的内容;而是要通过类型化数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
ArrayBuffer构造函数创建一个以字节为单位的给定长度的新 ArrayBuffer。你也可以从现有的数据(例如,从 Base64 字符串或者从本地文件获取数组缓冲区。
let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
let view = new Uint32Array(buffer); // 将 buffer 视为一个 32 位整数的序列
alert(Uint32Array.BYTES_PER_ELEMENT); // 每个整数 4 个字节
alert(view.length); // 4,它存储了 4 个整数
alert(view.byteLength); // 16,字节中的大小
// 让我们写入一个值
view[0] = 123456;
// 遍历值
for(let num of view) {
alert(num); // 123456,然后 0,0,0(一共 4 个值)
}
类型化数组(TypedArray)
定义
JavaScript 类型化数组是一种类似数组的对象,并提供了一种用于访问原始二进制数据的机制。
developer.mozilla.org/zh-CN/docs/…
js 在处理图像、音频视频编辑等方面,通常需要处理大量相同类型的数据,比如一张图片的像素点数据,坐标数据等等。而使用普通的 js 数组,存储的对象能动态增多和减少,并且可以存储任何 JavaScript 值,性能可能会有问题。所以 js 引入了类型化数组。
类型化数组的创建要明确数据类型和长度,所以处理起来效率会高很多,而且不支持 push pop 等操作。
DataView
newDataView(buffer,[byteOffset],[byteLength])
简单总结一下就是,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。
DataURL
生成 DataURL 的方式
HTMLCanvasElement.toDataURL()FileReader.readAsDataURL()
Endianness(字节序)
字节序,或字节顺序("Endian"、"endianness" 或 "byte-order"),描述了计算机如何组织字节,组成对应的数字。
每个内存存储位置都有一个索引或地址。每一个字节可以存储一个 8 位数字(即介于 0x00 和 0xff 之间),因此,你必须保留不止一个字节来储存一个更大的数字。现在,大部分需占用多个字节的数字排序方式是 little-endian(译者注:可称小字节序、低字节序,即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。与之对应的 big-endian 排列方式相反,可称大字节序、高字节序),所有的英特尔处理器都使用 little-endian。little-endian 的意思是使用低位储存更重要的信息,least-to-most-significant(最不重要的(least significant)字节取第一个位置,或者说地址最低的位置),可类比欧洲通用的日期书写方式(例如,31 December 2050。译者注:年份是最重要的,月份其次,日期最后)。
自然,big-endian 是相反的顺序,可类比 ISO 日期格式(例如 2050-12-31)。big-endian 通常被称作"网络字节顺序"("network byte order"), 因为互联网标准通常要求数据使用 big-endian 存储,从标准 Unix 套接字(socket)层开始,一直到标准化网络的二进制数据结构。此外,老式 Mac 计算机的 68000 系列 和 PowerPC(译者注:IBM 与 Apple 公司联合生产的个人台式机)微处理器曾使用 big-endian。
举个例子,用不同字节序存储数字 0x12345678(即十进制中的 305 419 896):
- little-endian:0x78 0x56 0x34 0x12
- big-endian:0x12 0x34 0x56 0x78
- mixed-endian(文物,非常罕见):0x34 0x12 0x78 0x56
服务器下载
const http = require('http')
const fs = require('fs')
const port = 3000
const server = http.createServer(async (req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/csv')
fs.readFile('./data1.xlsx',(err, data) => {
console.log(err, data )
res.end(data)
});
})
server.listen(port, () => {
console.log(`服务器运行在 http://127.0.0.1:${port}/`)
})
// 获取任何图像
let img = document.querySelector('img');
// 生成同尺寸的 <canvas>
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;
let context = canvas.getContext('2d');
// 向其中复制图像(此方法允许剪裁图像)
context.drawImage(img, 0, 0);
// 我们 context.rotate(),并在 canvas 上做很多其他事情
// toBlob 是异步操作,结束后会调用 callback
canvas.toBlob(function(blob) {
// blob 创建完成,下载它
let link = document.createElement('a');
link.download = 'example.png';
link.href = URL.createObjectURL(blob);
link.click();
// 删除内部 blob 引用,这样浏览器可以从内存中将其清除
URL.revokeObjectURL(link.href);
}, 'image/png');