评论

关于小程序自定义formData上传多文件的实战

关于小程序自定义formData上传多文件的实战

直接上代码:

/**
 * 模拟 FormData,手动构造 multipart/form-data 请求
 * @param {Object} params  - 文本字段,如 { name: 'xxx', age: 18 }
 * @param {Array} files    - 文件列表,每个元素是 { name: 'file字段名', path: '小程序文件路径', type: '文件MIME类型' }
 * @return {Object}        - { buffer: ArrayBuffer, header: { 'Content-Type': ... } }
 */
export function createFormData(params = {}, files = [],token = "") {
  // 生成唯一 boundary(可自定义更复杂规则)
  const boundary = `----WebKitFormBoundary${Date.now()}${Math.random().toString(36).substr(2)}`;
  const boundaryPrefix = `--${boundary}\r\n`;
  const endBoundary = `--${boundary}--\r\n`;
  let body = [];
  // 1. 处理文本参数
  Object.entries(params).forEach(([key, value]) => {
    let part =  `${boundaryPrefix}Content-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
    body.push(part);
  });
  // 2. 处理文件参数(多文件)
  return new Promise((resolve, reject) => {
    const filePromises = files.map(async (file) => {
      try {
        // 读取文件为 ArrayBuffer
        const fileRes =  wx.getFileSystemManager().readFileSync(
          file.url
        );
        const fileData = new Uint8Array(fileRes);
        let filename = file.url.match(/[^/]+$/)[0];
        // 拼接文件部分的请求体 files 是后端文件接收的参数名
        let fileParam = `${boundaryPrefix}Content-Disposition: form-data; name="files"; filename="${filename}"\r\nContent-Type: ${file.type}\r\n\r\n`;
        body.push(
            fileParam
        );
        body.push(fileData);
        body.push(`\r\n`);
      } catch (err) {
        reject(err);
      }
    });

    // 3. 所有文件处理完后,拼接最终请求体
    Promise.all(filePromises)
      .then(() => {
        // 拼接结尾 boundary
        body.push(endBoundary);
        // 合并所有部分为 ArrayBuffer
        const uint8Array = new Uint8Array(body.reduce((totalLength, part) => {
          if (typeof part === 'string') {
            // 字符串转 Uint8Array
            const strBytes = new TextEncoder().encode(part);
            totalLength += strBytes.length;
            return totalLength;
          } else {
            // Uint8Array 直接累加长度
            totalLength += part.length;
            return totalLength;
          }
        }, 0));
        let offset = 0;
        body.forEach((part) => {
          if (typeof part === 'string') {
            const strBytes = new TextEncoder().encode(part);
            uint8Array.set(strBytes, offset);
            offset += strBytes.length;
          } else {
            uint8Array.set(part, offset);
            offset += part.length;
          }
        });
        let header = {
          'Content-Type': `multipart/form-data; boundary=${boundary}`,
          //添加请求体token
          ...(token && { Authorization: `Bearer ${token}` })
        }
        // 返回可直接用于 wx.request 的数据和请求头
        resolve({
          buffer: uint8Array.buffer,
          header: header
        });
      })
      .catch(reject);
  });
}


创建formData并调用接口:

async submitComment() {
  //参数数据各自业务传递
  const formData = await createFormData({entity:JSON.stringify(form) },files,token);
  wx.request({
    url: url, // 后端上传接口
    method: 'POST',
    data: formData.buffer,
    header: {
      ...formData.header,
      // 若有 Token 等其他头信息,继续添加
      // 'Authorization': `Bearer ${token}`
    },
    success(res) {
    }
  })
}
      
      


图例:

最后一次编辑于  06-09  
点赞 0
收藏
评论
登录 后发表内容