Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
      • 一、文件上传方式总结
        • 1️⃣ 表单上传(最基础方式)
        • 2️⃣ Ajax 异步上传(前端主流方式)
        • 3️⃣ 使用第三方上传 SDK(云存储)
        • 4️⃣ 大文件分片上传(chunk upload)
        • 5️⃣ Base64 上传(小文件场景)
      • 🧭 上传功能优化点(面试重点)
      • 二、文件下载方式总结
        • 1️⃣ 直接下载链接(浏览器行为)
        • 2️⃣ 后端返回文件流(最常见)
        • 3️⃣ Blob 下载 + 文件名解析(推荐方式)
        • 4️⃣ Base64 文件下载
        • 5️⃣ 流式下载(后端分块输出)
      • ⚙️ 下载优化方向
      • 三、面试高频问答
      • 四、结构化思维导图总结
    • webpack优化实践
    • webpack基础应用知识总结
    • vue常见原理总结
    • vue基础知识总结
    • react高级特性
    • react基础知识总结
    • 微前端总结
    • 地图标绘--射线法来计算点在多边形内
    • web异常监控和分析
    • axios 与 promise
    • 前端优化指南
    • http缓存机制
    • 静态资源灰度发布
    • 小程序笔记
    • JS随机打乱数组
    • 非常实用的JavaScript一行代码
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2023-03-23
目录

上传下载文件方式总结

# 上传下载文件方式总结

—— 前端开发中文件上传与下载方式总结(含核心原理 + 案例 + 面试要点)。


# 一、文件上传方式总结

# 1️⃣ 表单上传(最基础方式)

<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">上传</button>
</form>
1
2
3
4

✅ 特点:

  • 浏览器原生支持
  • 适合简单的文件上传(无需 JS)
  • 表单 enctype 必须为 multipart/form-data

❌ 缺点:

  • 页面会刷新(非异步)
  • 不易控制上传进度

# 2️⃣ Ajax 异步上传(前端主流方式)

用fetch方式

const formData = new FormData();
formData.append('file', fileInput.files[0]);
// 关于formData不得不说一下,formData.append的参数只有三个,当设置value时只支持字符串类型和Blob类型
// The field's value. This can be a string or Blob (including subclasses such as File). If none of these are specified the value is converted to a string.

fetch('/upload', {
  method: 'POST',
  body: formData
}).then(res => res.json());
1
2
3
4
5
6
7
8
9

用XMLHttpRequest方式

// 检查是否支持FormData
if(window.FormData) { 
  var formData = new FormData();

  // 建立一个upload表单项,值为上传的文件
  formData.append('upload', document.getElementById('upload').files[0]);
  var xhr = new XMLHttpRequest();
  xhr.open('POST', $(this).attr('action'));
  // 定义上传完成后的回调函数
  xhr.onload = function () {
    if (xhr.status === 200) {
      console.log('上传成功');
    } else {
      console.log('出错了');
    }
  };
  xhr.send(formData);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

✅ 优点:

  • 无需刷新页面
  • 支持显示进度、断点续传
  • 可附带 token 或额外字段

📌 后端接收:

// Node + Express
app.post('/upload', upload.single('file'), (req, res) => {
  res.json({ success: true });
});
1
2
3
4

# 3️⃣ 使用第三方上传 SDK(云存储)

云厂商 上传方式 特点
阿里云 OSS ali-oss SDK 支持分片上传、签名直传
腾讯云 COS cos-js-sdk-v5 支持临时密钥、断点续传
七牛云 qiniu-js Base64 上传、表单上传

✅ 优势:

  • 减轻后端压力(前端直传到云)
  • 支持大文件分片上传与秒传

📌 直传流程:

  1. 前端向后端请求上传凭证(签名)
  2. 前端带签名上传到云存储
  3. 成功后回调/通知后端

# 4️⃣ 大文件分片上传(chunk upload)

// 切片
const chunkSize = 5 * 1024 * 1024; // 5MB
const file = input.files[0];
const chunks = [];

for (let i = 0; i < file.size; i += chunkSize) {
  chunks.push(file.slice(i, i + chunkSize));
}
1
2
3
4
5
6
7
8

上传:

chunks.forEach((chunk, index) => {
  const formData = new FormData();
  formData.append('file', chunk);
  formData.append('index', index);
  fetch('/upload', { method: 'POST', body: formData });
});
1
2
3
4
5
6

合并(后端):

// 接收到所有切片后合并
fs.appendFileSync('target.mp4', chunk);
1
2

✅ 优点:

  • 支持断点续传
  • 可显示进度、并行上传加速

# 5️⃣ Base64 上传(小文件场景)

const reader = new FileReader();
reader.onload = () => {
  const base64 = reader.result;
  fetch('/uploadBase64', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ data: base64 })
  });
};
reader.readAsDataURL(file);
1
2
3
4
5
6
7
8
9
10

✅ 优点:

  • 实现简单
  • 可直接用于存储头像、二维码等

❌ 缺点:

  • 体积变大约 33%
  • 不适合大文件

# 🧭 上传功能优化点(面试重点)

方向 说明
✅ 分片上传 适合大文件,支持断点续传
✅ 并行上传 多个分片同时上传,节省时间
✅ 秒传 通过文件 hash 检测是否已存在
✅ 上传进度 利用 XMLHttpRequest.onprogress
✅ 失败重试 网络异常可自动重传
✅ 前端校验 文件大小 / 类型限制
✅ 签名直传 提升安全性

# 二、文件下载方式总结

# 1️⃣ 直接下载链接(浏览器行为)

<a href="https://example.com/file.pdf" download>下载文件</a>
1

✅ 简单直接,无需 JS。 ⚠️ 同源限制下,跨域可能受限。


# 2️⃣ 后端返回文件流(最常见)

fetch('/api/download').then(res => res.blob()).then(blob => {
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'report.pdf';
  a.click();
  URL.revokeObjectURL(url);
});
1
2
3
4
5
6
7
8

也可以用form形式下载文件(只替换上面代码中间dom相关操作代码)

let myForm = document.createElement('form')
myForm.style.display = 'none'
myForm.setAttribute('target', '')
myForm.setAttribute('method', 'get')
myForm.setAttribute('action', url)
document.body.append(myForm);
myForm.submit();
1
2
3
4
5
6
7

📌 后端:

res.setHeader('Content-Disposition', 'attachment; filename="report.pdf"');
res.sendFile(filePath);
1
2

✅ 优点:

  • 可控制权限
  • 支持跨域下载

顺便一提,下载想要的规格的图片

function downloadImgURL(url, name) { 
  let image = new Image(); 
  image.setAttribute("crossOrigin", "anonymous"); 
  image.src = url; 
  image.onload = () => { 
    let canvas = document.createElement("canvas"); 
    canvas.width = image.width; 
    canvas.height = image.height; 
    let ctx = canvas.getContext("2d"); 
    ctx.drawImage(image, 0, 0, image.width, image.height); 
    canvas.toBlob(blob => { 
      let url = URL.createObjectURL(blob); 
      let a = document.createElement("a"); 
      a.download = name; 
      a.href = url; 
      a.click(); 
      a.remove(); // 用完释放URL对象 
      URL.revokeObjectURL(url); 
      }); 
    }; 
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3️⃣ Blob 下载 + 文件名解析(推荐方式)

fetch('/api/download').then(async (res) => {
  const blob = await res.blob();
  const filename = decodeURI(
    res.headers.get('Content-Disposition').split('filename=')[1]
    // 'Content-Type': 'application/octet-stream',// 这是一个二进制文件
  );
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
});
1
2
3
4
5
6
7
8
9
10
11
12

顺便一提:

在常规的 HTTP 响应中,Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

语法:

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
1
2
3

它的第一个参数可以是inline或者attachment。如果第一个参数是inline(默认值)则表示展示到网页中或者做为网页展示。如果第一个参数是attachment表示它应该被下载,大部分浏览器会出现一个保存为的对话框,如果提供了filename会用filename填充对话框的文件名。

Content-Disposition: attachment; filename="filename.jpg"
1

Content-Disposition可以触发“保存为”的对话框

200 OK
Content-Type: text/html; charset=utf-8
Content-Disposition: attachment; filename="cool.html"
Content-Length: 22

<HTML>Save me!</HTML>
1
2
3
4
5
6

上面的响应,不会做为简单的html处理,大多数浏览器会企图以cool.html保存这个文件

在HTML页面中通过POST请求发送Content-Type:multipart/form-data,下面是个例子

POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"

value2
--boundary--

1
2
3
4
5
6
7
8
9
10
11
12
13
14

关于如何自定义header,大家还是要知道一下的。

本质上跟上述的Content-Disposition差不多,只是我们这里不使用默认的header,我们自己自定义一个response header,跟后端决定好编码方式返回,前端直接获取这个自定义header,然后使用对应的解码即可,如使用decodeURIComponent。 但是我们都要知道,在跨域的情况下,前端获取到的header只有默认的6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。 所以你想要获取到别的header,需要后端配合,设置

Access-Control-Expose-Headers: Content-Disposition, custom-header
1

这样,前端就能获取到对应暴露的header字段,需要注意的是,Content-Disposition也是需要暴露的。


# 4️⃣ Base64 文件下载

const base64 = 'data:application/pdf;base64,...';
const a = document.createElement('a');
a.href = base64;
a.download = 'file.pdf';
a.click();
1
2
3
4
5

✅ 适用于小文件(例如导出图片、二维码等)。


# 5️⃣ 流式下载(后端分块输出)

用于大文件或导出报表时(如 zip、excel):

  • 后端使用流(stream)分块输出
  • 前端实时接收 blob 块
  • 可边下载边保存

# ⚙️ 下载优化方向

优化点 说明
✅ 使用 Blob 对象 避免内存暴涨
✅ Content-Disposition 后端返回文件名
✅ Content-Type 保证文件 MIME 类型正确
✅ 异步加载状态 提示“文件生成中...”
✅ 分块流式下载 大文件时防止阻塞

# 三、面试高频问答

Q1: 你在项目中如何实现大文件上传? 👉 分片 + 并行上传 + 断点续传 + 文件 hash 校验。

Q2: 如何显示上传进度? 👉 使用 xhr.upload.onprogress 或 fetch + progress event。

Q3: 如何实现秒传? 👉 上传前计算文件 hash,后端判断是否已存在该文件。

Q4: 文件下载如何防止浏览器直接预览? 👉 后端添加响应头:

res.setHeader('Content-Disposition', 'attachment; filename="xx.pdf"');
1

Q5: 上传文件如何防止安全问题? 👉 前端校验 + 后端 MIME 检查 + 文件隔离存储。


# 四、结构化思维导图总结

文件上传下载总结
├── 上传方式
│   ├── 表单上传(multipart/form-data)
│   ├── Ajax 上传(FormData)
│   ├── 云存储直传(带签名)
│   ├── 分片上传(chunk + merge)
│   ├── Base64 上传
│   └── 上传优化
│       ├── 秒传
│       ├── 并行上传
│       ├── 进度条
│       └── 重试机制
├── 下载方式
│   ├── 直接链接下载
│   ├── Blob 下载(fetch + blob)
│   ├── Base64 下载
│   ├── 流式下载(stream)
│   └── 下载优化
│       ├── 设置文件名
│       ├── 正确 Content-Type
│       ├── 防止预览
│       └── 大文件分块
└── 面试高频点
    ├── 分片上传原理
    ├── 秒传实现
    ├── 上传进度监听
    ├── Blob 下载实现
    └── Content-Disposition 作用
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

参考连接:

https://juejin.cn/post/6895599073676492807

https://www.jianshu.com/p/7636d5c60a8d

上次更新: 2025/11/26, 02:12:57
时区校正
webpack优化实践

← 时区校正 webpack优化实践→

Copyright © 2015-2025 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式