跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端

Axios 文件上传与下载实战指南

Axios 处理文件上传下载需配置 responseType 为 blob。上传使用 FormData 封装文件流,避免手动设置 Content-Type 导致 boundary 丢失。下载时需解析 Content-Disposition 获取文件名,支持中文编码。大文件需监听 onUploadProgress 或 onDownloadProgress 显示进度条。拦截器可统一处理 Token 及错误响应。异步导出场景建议轮询任务状态。注意跨域 CORS 配置及超时设置,防止内存溢出。

全栈工匠发布于 2026/3/24更新于 2026/8/1838 浏览
Axios 文件上传与下载实战指南

Axios 文件上传与下载实战指南

这玩意儿到底是个啥

Axios 是常用的 HTTP 库,但处理二进制流这块儿,很多开发者容易忽略。简单说,上传就是把文件塞进 FormData 里像寄快递一样打包发走。下载呢,就是告诉服务器'我要的是流',然后浏览器得知道怎么把这堆二进制数据变成能打开的文件。

先说说 Blob 这玩意儿是啥。Blob 全称 Binary Large Object,翻译过来就是'二进制大对象'。听起来高大上,其实就是浏览器用来存二进制数据的一个容器。你可以把它想象成一个虚拟的文件,存在内存里,用完了得手动清理,不然就像家里堆满了外卖盒,迟早要出事。

还有 FormData,这是 HTML5 新增的 API,专门用来模拟表单提交。为啥要用它?因为传统的 JSON 传不了文件啊!你试试把一张图片转成 base64 塞 JSON 里,那体积直接爆炸,服务器看了都想打人。

这里有个坑我得提前说:很多人以为文件上传必须用 POST,其实 PUT 也行,PATCH 也行,甚至 GET 理论上也能传(虽然有点变态)。但大部分场景下,POST 是最稳妥的,后端对接的时候也最省心。

上传文件那点破事

核心就是 FormData,这玩意儿就是个虚拟的表单,能把文件和普通字段混在一起塞进去。记得把 Content-Type 设成 multipart/form-data 吗?其实 Axios 会自动帮你搞,手贱去设反而容易翻车。

基础版:单文件上传

最朴素的上传长这样,但朴素不代表能用:

// 错误示范:手动设置 Content-Type,结果 boundary 丢了,后端直接懵逼
const uploadFile = (file) => {
  const formData = new FormData();
  formData.append('file', file);
  axios.post('/api/upload', formData, {
    headers: {
      'Content-Type': 'multipart/form-data' // 大错特错!
    }
  });
};

看到上面那个注释了吗?这就是新手常犯的错。FormData 的 boundary(边界符)是浏览器自动生成的,你手动设置 Content-Type 会把 boundary 覆盖掉,后端解析的时候就找不到文件从哪开始、到哪结束,直接报 Missing boundary 或者 Invalid boundary 的错误。

正确的姿势是让 Axios 自己处理,或者如果你用了拦截器统一设置 headers,记得在上传请求里删掉 Content-Type:

// 正确版本
const uploadFile = async (file) => {
  const formData = new FormData();
  // 第一个参数是字段名,要跟后端约定好,别瞎写
  formData.append('file', file);
  // 还可以塞点其他数据,比如用户 ID、业务类型啥的
  formData.append('userId', '9527');
  formData.append('bizType', 'avatar');
  try {
    const response = await axios.post('/api/upload', formData, {
      // 这里啥也不用设,Axios 看到 FormData 会自动搞 Content-Type
      // 但如果你全局拦截器加了 Content-Type,得在这里覆盖掉
      headers: {
        'Content-Type': undefined // 让浏览器自己发挥
      }
    });
    console.log('上传成功:', response.data);
    return response.data;
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
};

进阶版:多文件上传

多文件上传其实就是循环往 FormData 里 append,没啥高科技,别想太复杂。但这里有个细节:同一个字段名塞多个文件,后端能不能接住要看人家用的什么框架。

// 多文件上传,后端是 Node.js + multer 或者 Java + SpringBoot 的情况
const uploadMultipleFiles = async (fileList) => {
  const formData = new FormData();
  // 方法 1:同一个字段名塞多个,后端用数组接收
  fileList.forEach((file, index) => {
    // 字段名都叫 files,后端接收 files[]
    formData.append('files', file);
    // 如果想保留原始文件名信息,可以额外传
    formData.append(`fileNames[${index}]`, file.name);
  });
  // 方法 2:不同字段名,适合每个文件有不同业务含义的场景
  // formData.append('idCard', fileList[0]);
  // formData.append('bankCard', fileList[1]);
  try {
    const response = await axios.post('/api/upload/batch', formData, {
      headers: {
        'Content-Type': undefined
      }
    });
    return response.data;
  } catch (error) {
    console.error('批量上传失败:', error);
    throw error;
  }
};

高阶版:带进度条的上传

大文件上传怎么办?总不能让用户对着进度条发呆半小时吧,得搞个上传进度监听,让用户知道还没死机。Axios 提供了 onUploadProgress 回调,这个 API 底层用的是 XMLHttpRequest 的 progress 事件。

const uploadWithProgress = (file, onProgress) => {
  const formData = new FormData();
  formData.append('file', file);
  return axios.post('/api/upload', formData, {
    headers: {
      'Content-Type': undefined
    },
    // 关键配置:上传进度回调
    onUploadProgress: (progressEvent) => {
      // progressEvent 里有 loaded 和 total,单位是字节
      const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      console.log(`上传进度:${percentCompleted}%`);
      // 把进度抛给外部,用来更新 UI
      if (onProgress) {
        onProgress(percentCompleted);
      }
    },
    // 大文件记得调大超时时间,或者设 0 不限制
    timeout: 0
  });
};

// 在 React 组件里用
const FileUploadComponent = () => {
  const [progress, setProgress] = useState(0);
  const [uploading, setUploading] = useState(false);
  
  const handleUpload = async (file) => {
    setUploading(true);
    setProgress(0);
    try {
      await uploadWithProgress(file, (percent) => {
        setProgress(percent);
      });
      message.success('上传成功!');
    } catch (error) {
      message.error('上传失败,请重试');
    } finally {
      setUploading(false);
    }
  };
  
  return (
    <div>
      <Upload beforeUpload={handleUpload} fileList={[]}>
        <Button icon={<UploadOutlined />} loading={uploading}>点击上传</Button>
      </Upload>
      {uploading && <Progress percent={progress} status="active" />}
    </div>
  );
};

防手贱:防抖处理

上传的时候加个防抖,防止用户手残狂点按钮,瞬间发起几百个请求把服务器打挂。这玩意儿在测试环境可能没事,上了生产要是被压测或者恶意点击,直接 P0 事故。

import { debounce } from 'lodash';

// 封装一个带防抖的上传函数
const debouncedUpload = debounce(async (file, callback) => {
  try {
    const result = await uploadWithProgress(file, callback);
    return result;
  } catch (error) {
    console.error(error);
  }
}, 300, { leading: true, trailing: false });

// 首次点击立即执行,后面 300ms 内的点击忽略
// 或者更粗暴点,直接在上传期间禁用按钮
const UploadButton = () => {
  const [isUploading, setIsUploading] = useState(false);
  
  const handleClick = async () => {
    if (isUploading) return; // 简单粗暴但有效
    setIsUploading(true);
    try {
      await uploadFile(selectedFile);
    } finally {
      setIsUploading(false);
    }
  };
  
  return <Button loading={isUploading} onClick={handleClick}>上传</Button>;
};

下载文件才是真·深水区

重点来了!请求头里必须加 responseType: 'blob',不然你拿到的就是一堆乱码字符串,神仙也救不了。这坑我踩过不止一次,后端明明返回的是二进制流,我打印 response.data 一看,满屏的乱码,当时就想砸键盘。

最简版:基础下载

// 错误的示范,千万别这么写
const wrongDownload = async () => {
  const res = await axios.get('/api/download/excel');
  // res.data 是一堆乱码字符串,因为 axios 默认把响应当 JSON 解析了
  console.log(res.data); // "..."
};

// 正确的打开方式
const downloadExcel = async () => {
  try {
    const response = await axios({
      url: '/api/export/user-list',
      method: 'GET',
      responseType: 'blob', // 关键!告诉 Axios 我要二进制流
      params: {
        startDate: '2024-01-01',
        endDate: '2024-12-31'
      }
    });
    // 现在 response.data 是个 Blob 对象
    const blob = new Blob([response.data], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    // 造个临时链接
    const url = window.URL.createObjectURL(blob);
    // 创建隐藏的 a 标签
    const link = document.createElement('a');
    link.href = url;
    link.download = '用户列表.xlsx'; // 指定下载文件名
    document.body.appendChild(link);
    // 触发点击
    link.click();
    // 清理现场,这步很重要!
    document.body.removeChild(link);
    window.URL.revokeObjectURL(url);
  } catch (error) {
    console.error('下载失败:', error);
  }
};

文件名怎么搞?

上面那个例子文件名是写死的,实际项目中肯定要从后端拿。后端通常在 Content-Disposition 里藏了个小秘密,格式大概是 attachment; filename="xxx.xlsx" 或者 attachment; filename*=UTF-8''xxx.xlsx(后者是为了支持中文文件名)。

const downloadWithFileName = async () => {
  const response = await axios({
    url: '/api/export/report',
    method: 'POST',
    responseType: 'blob',
    data: {
      reportType: 'monthly'
    }
  });
  
  // 从响应头里抠文件名
  const contentDisposition = response.headers['content-disposition'];
  let fileName = '下载文件.xlsx'; // 给个默认值
  
  if (contentDisposition) {
    // 先尝试匹配 filename*="UTF-8''xxx" 这种格式(RFC 5987)
    const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
    if (utf8Match && utf8Match[1]) {
      // 需要 decodeURIComponent,因为中文被编码了
      fileName = decodeURIComponent(utf8Match[1]);
    } else {
      // 再尝试普通的 filename="xxx"
      const normalMatch = contentDisposition.match(/filename="([^"]+)"/i);
      if (normalMatch && normalMatch[1]) {
        fileName = normalMatch[1];
      }
    }
  }
  
  const blob = new Blob([response.data], {
    type: response.headers['content-type']
  });
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = fileName;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  window.URL.revokeObjectURL(url);
};

这里有个坑要注意:如果后端没配置暴露 Content-Disposition 头,你前端是读不到的!axios 的 response.headers 里会没有这个字段。这时候得让后端在 CORS 配置里加 Access-Control-Expose-Headers: Content-Disposition。

封装一个通用的下载函数

把创建 a 标签、清理 URL 这些脏活累活全包了,以后调用就一行代码。这种工具函数一定要提前写好,别等到项目里到处都是复制粘贴的下载代码,改起来想死。

/**
 * 通用文件下载函数
 * @param {string} url - 下载地址
 * @param {object} config - axios 配置
 * @param {string} defaultName - 默认文件名
 */
export const downloadFile = async (url, config = {}, defaultName = 'download') => {
  try {
    const response = await axios({
      url,
      method: 'GET',
      responseType: 'blob',
      ...config
    });
    
    // 提取文件名
    const contentDisposition = response.headers['content-disposition'];
    let fileName = defaultName;
    if (contentDisposition) {
      const match = contentDisposition.match(/filename\*?=(?:UTF-8'')?([^;]+)/i);
      if (match) {
        fileName = decodeURIComponent(match[1].replace(/['"]/, ''));
      }
    }
    
    // 创建下载链接
    const blob = new Blob([response.data], {
      type: response.headers['content-type'] || 'application/octet-stream'
    });
    const downloadUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = downloadUrl;
    link.download = fileName;
    link.style.display = 'none';
    document.body.appendChild(link);
    link.click();
    
    // 延迟清理,确保下载已经开始
    setTimeout(() => {
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    }, 100);
    
    return { success: true, fileName };
  } catch (error) {
    console.error('下载失败:', error);
    // 如果后端返回的是 JSON 错误信息(比如权限不足),需要特殊处理
    if (error.response && error.response.data instanceof Blob) {
      const text = await error.response.data.text();
      try {
        const errorData = JSON.parse(text);
        throw new Error(errorData.message || '下载失败');
      } catch {
        throw new Error('下载失败');
      }
    }
    throw error;
  }
};

// 使用示例,一行代码搞定
// downloadFile('/api/export/users', { params: { dept: 'tech' } }, '用户列表.xlsx');

带下载进度的大文件下载

上传有进度条,下载当然也可以有。不过下载的进度回调叫 onDownloadProgress,用法一模一样。

const downloadLargeFile = async (url, fileName, onProgress) => {
  const response = await axios({
    url,
    method: 'GET',
    responseType: 'blob',
    onDownloadProgress: (progressEvent) => {
      if (progressEvent.total) {
        const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        onProgress?.(percent);
      }
    },
    timeout: 0 // 大文件不设超时
  });
  
  // 保存文件
  const blob = new Blob([response.data]);
  const downloadUrl = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = downloadUrl;
  link.download = fileName;
  link.click();
  window.URL.revokeObjectURL(downloadUrl);
};

// 在组件里用,加个进度条
const DownloadButton = () => {
  const [progress, setProgress] = useState(0);
  const [downloading, setDownloading] = useState(false);
  
  const handleDownload = async () => {
    setDownloading(true);
    setProgress(0);
    try {
      await downloadLargeFile('/api/export/big-data', '大数据报表.xlsx', (percent) => setProgress(percent));
    } catch (error) {
      message.error('下载失败');
    } finally {
      setDownloading(false);
    }
  };
  
  return (
    <div>
      <Button onClick={handleDownload} loading={downloading}>下载大文件</Button>
      {downloading && <Progress percent={progress} />}
    </div>
  );
};

咱得客观聊聊这方案

优点嘛,Axios 生态好,拦截器用起来爽,统一处理错误和 Token 方便得一匹。但缺点也不是没有,咱得实话实说。

优点

1. 拦截器真香 可以在请求发送前统一加 Token,响应回来后统一处理错误,文件下载上传也能享受到这个便利。

// 请求拦截器:自动加 Token
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:统一处理 401
axios.interceptors.response.use(response => response, error => {
  if (error.response?.status === 401) {
    // Token 过期,踢去登录页
    window.location.href = '/login';
  }
  return Promise.reject(error);
});

2. 错误处理统一 文件下载的时候,如果后端返回 401 或者 500,你拿到的是个 Blob,需要特殊处理才能读到错误信息。这个逻辑可以封装在拦截器里:

axios.interceptors.response.use(
  async response => {
    // 如果是文件下载请求,且状态不对,尝试解析错误信息
    if (response.config.responseType === 'blob' && response.status !== 200) {
      const text = await response.data.text();
      try {
        const error = JSON.parse(text);
        return Promise.reject(error);
      } catch {
        return Promise.reject(new Error('下载失败'));
      }
    }
    return response;
  },
  error => Promise.reject(error)
);

缺点

1. 大文件内存爆炸 前面说的 Blob 下载,是把整个文件塞进内存的。如果你要下载几个 G 的日志文件,浏览器直接卡死。这时候得用流式下载或者后端直接返回下载链接,让浏览器自己去下。

2. 兼容性坑 虽然现在 IE 都进博物馆了,但有些老旧安卓机上的 WebView 还是偶尔抽风,得留个心眼。特别是 URL.createObjectURL 和 URL.revokeObjectURL,在部分 WebView 里可能有内存泄漏问题。

3. 无法暂停续传 Axios 本身不支持断点续传,如果要实现暂停、继续功能,得用更底层的 XMLHttpRequest 或者专门的库比如 resumablejs。

真实项目里怎么落地

光讲代码不讲场景就是耍流氓,说说我在实际项目里遇到的几个典型场景。

场景一:报表导出(异步生成)

后台管理系统里的报表导出,数据量通常很大,不可能点一下立马下完。通常配合定时任务,用户点一下,后端生成好了再通知前端去拉,别傻等。

// 流程:点击导出 -> 后端返回任务 ID -> 轮询任务状态 -> 完成后下载
const exportReport = async (params) => {
  // 1. 发起导出任务
  const { data: { taskId } } = await axios.post('/api/export/task', params);
  
  // 2. 轮询任务状态
  const checkStatus = () => new Promise((resolve, reject) => {
    const timer = setInterval(async () => {
      try {
        const { data } = await axios.get(`/api/export/task/${taskId}/status`);
        if (data.status === 'completed') {
          clearInterval(timer);
          resolve(data.downloadUrl);
        } else if (data.status === 'failed') {
          clearInterval(timer);
          reject(new Error('生成失败'));
        }
        // 还在处理中,继续等
      } catch (error) {
        clearInterval(timer);
        reject(error);
      }
    }, 2000); // 每 2 秒查一次
  });
  
  try {
    const downloadUrl = await checkStatus();
    // 3. 下载文件
    await downloadFile(downloadUrl, {}, '报表.xlsx');
  } catch (error) {
    message.error(error.message);
  }
};

场景二:批量导入 + 实时预览

批量导入用户数据,上传完立马解析预览,错了哪行高亮显示,这种体验才叫丝滑。

const UploadWithPreview = () => {
  const [previewData, setPreviewData] = useState([]);
  const [errorRows, setErrorRows] = useState([]);
  
  const handleUpload = async (file) => {
    const formData = new FormData();
    formData.append('file', file);
    
    // 先上传到预览接口,不真正入库
    const { data } = await axios.post('/api/import/preview', formData, {
      headers: {
        'Content-Type': undefined
      }
    });
    
    setPreviewData(data.list);
    setErrorRows(data.errors); // 错误行号
    
    if (data.errors.length > 0) {
      message.warning(`发现${data.errors.length}行错误,请修改后重新上传`);
    }
  };
  
  const confirmImport = async () => {
    // 确认无误后,真正导入
    await axios.post('/api/import/confirm', { data: previewData });
    message.success('导入成功');
  };
  
  return (
    <div>
      <Upload beforeUpload={handleUpload} fileList={[]}>
        <Button>上传 Excel 预览</Button>
      </Upload>
      {previewData.length > 0 && (
        <>
          <Table dataSource={previewData} rowClassName={(record, index) => errorRows.includes(index) ? 'error-row' : ''} />
          <Button type="primary" onClick={confirmImport}>确认导入</Button>
        </>
      )}
    </div>
  );
};

场景三:图片压缩上传

图片或者附件上传,记得做本地压缩和格式校验,别等传到服务器报错了再让用户重传,那是友尽的节奏。

import Compressor from 'compressorjs';

const compressAndUpload = (file) => {
  // 先校验
  if (!['image/jpeg', 'image/png'].includes(file.type)) {
    message.error('只支持 jpg/png 格式');
    return;
  }
  if (file.size > 10 * 1024 * 1024) {
    message.error('图片不能超过 10MB');
    return;
  }
  
  // 压缩
  new Compressor(file, {
    quality: 0.6, // 压缩质量
    maxWidth: 1920, // 最大宽度
    maxHeight: 1080,
    success(result) {
      // result 是压缩后的 Blob,转成 File 再上传
      const compressedFile = new File([result], file.name, {
        type: result.type,
        lastModified: Date.now()
      });
      uploadFile(compressedFile);
    },
    error(err) {
      message.error('压缩失败');
    }
  });
};

遇到报错别只会重启

最常见就是下载下来文件打不开,99% 是 responseType 没设对,或者后端返回的不是流而是 JSON 报错信息,得先判断一下。

下载下来是乱码或打不开

// 排查步骤 1:检查 responseType
axios.get('/api/download', { responseType: 'blob' })

// 排查步骤 2:检查后端是否真的返回了流
// 有时候后端出错了,返回的是 JSON 错误信息,但 Content-Type 还是 application/json
// 这时候你强行转 Blob,下载下来就是个包含错误信息的文本文件
// 安全做法:先判断一下是不是真的 Blob
const safeDownload = async () => {
  const response = await axios.get('/api/download', {
    responseType: 'blob', // 这个配置很重要,让 axios 把原始响应透传过来,不要自动转换
    transformResponse: []
  });
  
  // 检查 Content-Type
  const contentType = response.headers['content-type'];
  if (contentType.includes('application/json')) {
    // 后端返回的是 JSON,可能是错误信息
    const text = await response.data.text();
    const error = JSON.parse(text);
    throw new Error(error.message);
  }
  
  // 确定是文件流,继续下载流程
  // ...
};

跨域问题

OPTIONS 预检请求要是挂了,文件根本发不出去,赶紧找后端查 CORS 配置。文件上传的 CORS 配置要特别注意:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
Access-Control-Expose-Headers: Content-Disposition # 这个必须加,否则前端读不到文件名

超时问题

大文件上传下载一定要把 timeout 调大点,或者干脆设成 0 无限等待(当然最好还是做进度条)。

// 针对不同接口设置不同超时
const api = axios.create({
  baseURL: '/api',
  timeout: 10000 // 默认 10 秒
});

// 上传接口超时设长点
api.post('/upload', formData, {
  timeout: 300000 // 5 分钟
});

// 下载接口根据文件大小动态设置
api.get('/download/big-file', {
  responseType: 'blob',
  timeout: 0 // 不限制
});

几个让同事喊 666 的骚操作

1. 全局上传下载管理器

搞个全局的上传下载管理器,显示所有进行中的任务,暂停、取消功能安排上,逼格满满。

// 用 Context 或者 Redux 管理全局状态
const DownloadManager = () => {
  const [tasks, setTasks] = useState([]);
  
  const addTask = (url, fileName) => {
    const taskId = Date.now();
    const newTask = {
      id: taskId,
      fileName,
      progress: 0,
      status: 'pending', // pending, downloading, completed, error
      controller: new AbortController() // 用于取消请求
    };
    setTasks(prev => [...prev, newTask]);
    
    // 开始下载
    axios({
      url,
      responseType: 'blob',
      signal: newTask.controller.signal, // 绑定取消信号
      onDownloadProgress: (e) => {
        const percent = Math.round((e.loaded * 100) / e.total);
        updateTaskProgress(taskId, percent);
      }
    }).then(response => {
      // 保存文件...
      updateTaskStatus(taskId, 'completed');
    }).catch(error => {
      if (error.name === 'CanceledError') {
        updateTaskStatus(taskId, 'cancelled');
      } else {
        updateTaskStatus(taskId, 'error');
      }
    });
    
    return taskId;
  };
  
  const cancelTask = (taskId) => {
    const task = tasks.find(t => t.id === taskId);
    if (task && task.controller) {
      task.controller.abort(); // 取消请求
    }
  };
  
  return (
    <div className="download-manager">
      {tasks.map(task => (
        <div key={task.id} className="task-item">
          <span>{task.fileName}</span>
          <Progress percent={task.progress} />
          {task.status === 'downloading' && (
            <Button onClick={() => cancelTask(task.id)}>取消</Button>
          )}
        </div>
      ))}
    </div>
  );
};

2. 利用拦截器统一处理

// 请求拦截器:给所有上传请求加特定标识
axios.interceptors.request.use(config => {
  if (config.data instanceof FormData) {
    config.headers['X-Upload-Request'] = 'true'; // 上传请求不设置超时,或者设置很长
    config.timeout = 0;
  }
  return config;
});

// 响应拦截器:统一提取下载文件名
axios.interceptors.response.use(response => {
  if (response.config.responseType === 'blob') {
    const disposition = response.headers['content-disposition'];
    if (disposition) {
      // 把文件名挂到 response 上,方便业务代码取用
      response.fileName = extractFileName(disposition);
    }
  }
  return response;
});

3. 文件类型校验工具函数

const FILE_TYPES = {
  image: ['image/jpeg', 'image/png', 'image/gif'],
  excel: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
  word: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']
};

const validateFile = (file, type, maxSize = 10 * 1024 * 1024) => {
  const errors = [];
  if (!FILE_TYPES[type].includes(file.type)) {
    errors.push(`文件类型错误,请上传${type}格式文件`);
  }
  if (file.size > maxSize) {
    errors.push(`文件大小不能超过${maxSize / 1024 / 1024}MB`);
  }
  return {
    valid: errors.length === 0,
    errors
  };
};

// 使用
const { valid, errors } = validateFile(file, 'excel', 5 * 1024 * 1024);
if (!valid) {
  message.error(errors.join(','));
}

总结

技术这东西,有时候真不是越新越好,Axios 这老家伙在文件处理上依然稳如老狗。别光收藏不吃灰,赶紧去项目里试两把,万一明天产品经理就提了这个需求呢?到时候你能淡定地说'这个简单,半天搞定',而不是'我研究研究',那逼格完全不一样。

要是真踩了坑也别骂街,毕竟咱们前端就是在填坑和造坑之间反复横跳,痛并快乐着嘛。行了,不多哔哔,你们要是试的过程中遇到啥奇葩问题,欢迎在群里吐槽,咱们一起研究。记住,能准时干饭的前端才是好前端,共勉!

目录

  1. Axios 文件上传与下载实战指南
  2. 这玩意儿到底是个啥
  3. 上传文件那点破事
  4. 基础版:单文件上传
  5. 进阶版:多文件上传
  6. 高阶版:带进度条的上传
  7. 防手贱:防抖处理
  8. 下载文件才是真·深水区
  9. 最简版:基础下载
  10. 文件名怎么搞?
  11. 封装一个通用的下载函数
  12. 带下载进度的大文件下载
  13. 咱得客观聊聊这方案
  14. 优点
  15. 缺点
  16. 真实项目里怎么落地
  17. 场景一:报表导出(异步生成)
  18. 场景二:批量导入 + 实时预览
  19. 场景三:图片压缩上传
  20. 遇到报错别只会重启
  21. 下载下来是乱码或打不开
  22. 跨域问题
  23. 超时问题
  24. 几个让同事喊 666 的骚操作
  25. 1. 全局上传下载管理器
  26. 2. 利用拦截器统一处理
  27. 3. 文件类型校验工具函数
  28. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Openclaw + 飞书多机器人配置实战指南
  • AI 如何重塑产品经理的工作模式与核心能力
  • 2026 年高校 AIGC 检测新规解读及 AI 率合格标准
  • VSCode 配置 Git 忽略文件及文件夹的多种方法
  • Python 量化实战:使用 AKShare 获取 A 股实时行情数据
  • Mac 端 Git 基础概念与 PyCharm 使用指南
  • llama.cpp 与 llama-server 安装部署指南
  • Java 面试核心知识点汇总:基础、并发、框架等
  • 从零开始搭建 Trae 的 Java 开发环境
  • 基于 Neo4j 和 py2neo 的知识图谱搭建步骤详解
  • Qt 键盘输入法开源方案
  • Python逆向工程实战:解密PyInstaller可执行文件的字节码恢复技术
  • AI 眼镜中的 SD NAND 存储与 SOC 芯片协同架构
  • RAG 进阶:多模态图片检索技术实践
  • Claude Code 本地接入备忘:用第三方 API 代理配置 CLI 工具
  • 深度生成模型对比:VAE、GAN、AR、Flow 与 Diffusion 原理及代码实战
  • PHP 实现长轮询消息实时推送功能
  • FPGA 实现 OV5640 摄像头视频采集与 VGA 显示
  • 前端状态管理:Redux、Zustand 与 Jotai 方案对比
  • AI 编程工具全方位对比:Copilot、Cursor 等主流工具选型指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online