10.4FormData :前端文件上传与表单数据处理的核心工具

什么是 FormData

FormData 是一个 JavaScript 对象,用于表示 HTML 表单数据。它可以模拟表单控件的键值对,并支持 字符串二进制数据(如文件) 的混合提交。

✅ 主要用途:

  • 文件上传(图片、视频、文档等)
  • 提交包含文件的复杂表单
  • 构造 multipart/form-data 请求体
  • fetchXMLHttpRequestaxios 配合使用

创建 FormData 实例

1. 空的 FormData

复制代码
const formData = new FormData();

2. 从现有 <form> 元素创建

复制代码
<form id="myForm">
  <input name="username" value="Alice" />
  <input type="file" name="avatar" />
</form>

const form = document.getElementById('myForm');
const formData = new FormData(form);
// 自动包含所有表单字段

三、添加数据:append(key, value, filename?)

语法:

复制代码
formData.append(key, value, filename);
参数 说明
key 字段名(字符串)
value 值,可以是字符串、BlobFile
filename 可选,上传时的文件名(仅当 value 是 Blob/File 时有效)

示例:

复制代码
const formData = new FormData();

// 添加字符串
formData.append('username', 'Alice');
formData.append('email', 'alice@example.com');

// 添加文件(来自 input)
const fileInput = document.querySelector('input[type=file]');
const file = fileInput.files[0];
formData.append('avatar', file, 'my-avatar.jpg'); // 指定文件名

// 添加 Blob(如 canvas 截图)
canvas.toBlob(blob => {
  formData.append('screenshot', blob, 'screen.png');
});

// 添加多文件(同名)
formData.append('photos', file1);
formData.append('photos', file2);

读取数据的方法

由于 FormData 不是普通对象,不能用 formData.key 取值。

1. formData.get(key) ------ 获取第一个匹配的值

适用于单值字段(如 name、email 等)

复制代码
const formData = new FormData();
formData.append('username', 'Alice');
formData.append('avatar', file); // file 是 File 对象

console.log(formData.get('username')); // "Alice"
console.log(formData.get('avatar'));   // File 对象

⚠️ 注意:如果同一个 key 有多个值,get() 只返回第一个


2. formData.getAll(key) ------ 获取某个 key 的所有值

适用于多文件上传或复选框等场景

复制代码
formData.append('photos', file1);
formData.append('photos', file2);

console.log(formData.getAll('photos')); 
// [File, File]

3. formData.has(key) ------ 判断是否包含某个 key
复制代码
if (formData.has('username')) {
  console.log('包含用户名');
}

4. 遍历所有数据:使用 formData.entries()(推荐)
复制代码
for (let [key, value] of formData.entries()) {
  console.log(key, value);
  // 示例输出:
  // username  "Alice"
  // avatar    File { name: "avatar.jpg", ... }
}

// 遍历所有键值对
for (let [key, value] of formData.entries()) {
  console.log(key, value);
}

// 只遍历键
for (let key of formData.keys()) {
  console.log(key);
}

// 只遍历值
for (let value of formData.values()) {
  console.log(value);
}

✅ 这是最常用的方式,适合调试或序列化所有数据。


5. 转成普通对象(仅限字符串值,不推荐用于文件
复制代码
function formDataToObject(formData) {
  const obj = {};
  for (let [key, value] of formData.entries()) {
    // 注意:如果是 File 对象,这里会变成 [object File]
    obj[key] = value;
  }
  return obj;
}

const obj = formDataToObject(formData);
console.log(obj); // { username: "Alice", avatar: File }

⚠️ 注意:如果值是 FileBlob,转成对象后无法还原为原始文件内容,仅用于查看结构

修改与删除

1. set(key, value, filename?) ------ 设置值(会覆盖已有)

复制代码
formData.set('username', 'Bob'); // 替换所有 username 的值

⚠️ 注意:set() 会删除所有同名字段,再添加新值。

2. delete(key) ------ 删除某个字段的所有值

复制代码
formData.delete('tempImage');

发送请求(与 fetch / axios 配合)

✅ 使用 fetch

复制代码
//原生 fetch 不支持上传进度事件(不像 axios 的 onUploadProgress)

<input type="text" id="username" value="Alice" />
<input type="file" id="avatar" />
<button onclick="upload()">上传</button>

async function upload() {
  const username = document.getElementById('username').value;
  const fileInput = document.getElementById('avatar');
  const file = fileInput.files[0];

  if (!file) {
    alert('请先选择文件');
    return;
  }

  // 创建 FormData
  const formData = new FormData();
  formData.append('username', username);
  formData.append('avatar', file); // 字段名需与后端一致

  try {
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData
      // ⚠️ 不要设置 Content-Type!
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const result = await response.json();
    console.log('上传成功:', result);
  } catch (error) {
    console.error('上传失败:', error);
  }
}

✅ 使用 axios

复制代码
<input type="file" id="avatar" />
<input type="text" id="username" value="Alice" />
<button onclick="upload()">上传</button>



async function upload() {
  const fileInput = document.getElementById('avatar');
  const usernameInput = document.getElementById('username');

  const file = fileInput.files[0];
  const username = usernameInput.value;

  if (!file) {
    alert('请选择文件');
    return;
  }

  // 创建 FormData
  const formData = new FormData();
  formData.append('username', username);
  formData.append('avatar', file); // 字段名需与后端一致

  try {
    const response = await axios.post('/api/upload', formData, {
      // ⚠️ 注意:不要设置 Content-Type
      // headers: {
      //   'Content-Type': 'multipart/form-data'  // ❌ 错误!会破坏 boundary
      // },
      onUploadProgress: (progressEvent) => {
        const percent = (progressEvent.loaded / progressEvent.total) * 100;
        console.log(`上传进度: ${percent.toFixed(2)}%`);
      }
    });

    console.log('上传成功:', response.data);
  } catch (error) {
    console.error('上传失败:', error.response?.data || error.message);
  }
}

🔔 重要 :不要手动设置 Content-Type


📂 七、文件处理技巧

1. 从网络图片创建 File 并添加

复制代码
async function addImageFromURL(url) {
  const response = await fetch(url);
  const blob = await response.blob();
  const file = new File([blob], 'online.jpg', { type: blob.type });
  formData.append('image', file);
}

2. 限制文件类型/大小

复制代码
const file = fileInput.files[0];
if (file.size > 2 * 1024 * 1024) {
  alert('文件不能超过 2MB');
  return;
}
if (!['image/jpeg', 'image/png'].includes(file.type)) {
  alert('只支持 JPG/PNG');
  return;
}
formData.append('photo', file);

常见注意事项

问题 解决方案
手动设置 Content-Type ❌ 不要设置,让浏览器自动设置
无法用 formData.key 取值 ✅ 使用 get() / getAll()
FormData 不是响应式(Vue) ✅ 用普通数据管理,最后生成 FormData
跨域请求携带 cookie fetchcredentials: 'include'axioswithCredentials: true
后端收不到文件 ✅ 检查字段名是否匹配,是否用了 multer 等中间件
相关推荐
AntBlack4 小时前
不当韭菜 : 好像真有点效果 ,想藏起来自己用了
前端·后端·python
楊无好4 小时前
react中props的使用
前端·react.js·前端框架
一个处女座的程序猿O(∩_∩)O4 小时前
Vue-Loader 深度解析:原理、使用与最佳实践
前端·javascript·vue.js
一点一木4 小时前
火山方舟 Responses API 实战指南:从概念到「公司尽调 Dossier 生成器」
前端·人工智能·api
还是大剑师兰特5 小时前
TypeScript 面试题及详细答案 100题 (91-100)-- 工程实践与框架集成
前端·javascript·typescript·1024程序员节
用户47949283569155 小时前
typeof null === 'object':JavaScript 最古老的 bug 为何 30 年无法修复?
前端·javascript·面试
__WanG5 小时前
如何编写标准StatefulWidget页面
前端·flutter
非凡ghost5 小时前
By Click Downloader(下载各种在线视频) 多语便携版
前端·javascript·后端
非凡ghost5 小时前
VisualBoyAdvance-M(GBA模拟器) 中文绿色版
前端·javascript·后端