所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

使用AJAX实现文件上传|详细教程与核心问题解决技巧

使用AJAX实现文件上传|详细教程与核心问题解决技巧 一

文章目录CloseOpen

AJAX上传文件的基础逻辑:从FormData到请求发送

其实AJAX上传文件的核心就一个“小工具”——FormData。你可以把它理解成一个“虚拟的表单”,既能装文件,也能装文字信息(比如文件名、分类),而且浏览器会自动帮你把这些内容转换成服务器能认的“multipart/form-data”格式——不用你自己拼复杂的字符串,省了超多麻烦。

我第一次用的时候,还傻兮兮地手动构造请求体:把文件转成Base64字符串,再拼到JSON里发出去,结果后端根本解析不了。后来查了MDN才知道,FormData是浏览器专门为“上传文件”设计的——它能完美模拟传统表单的提交方式,但又不会刷新页面。

具体怎么做呢?我拆成5步,你跟着走肯定没错:

  • 拿到用户选的文件:先放一个标签,然后用JS监听它的change事件——当用户选好文件,e.target.files[0]就是你要的文件对象(比如头像、商品图)。
  • 创建FormData并加文件let formData = new FormData();然后用formData.append('file', file)把文件加进去——要是你还想传点其他信息(比如“这是商品图”),可以再加一句formData.append('type', 'product_img'),特别方便。
  • 初始化XHR对象let xhr = new XMLHttpRequest();——这是AJAX的“核心引擎”,负责发请求和收响应。
  • 设置请求参数xhr.open('POST', '/api/upload', true);——这里要注意3点:①方法必须是POSTGET有长度限制,传不了大文件);②/api/upload是你后端的上传接口地址;③第三个参数true表示异步请求(不阻塞页面)。
  • 发送请求并监听结果xhr.send(formData);之后要加两个事件监听:xhr.onload(请求成功完成时触发)和xhr.onerror(请求失败时触发)。比如:
  •  xhr.onload = function() {
    

    if (xhr.status === 200) {

    alert('上传成功!');

    // 这里可以更新页面,比如显示上传的图片

    }

    };

    xhr.onerror = function() {

    alert('上传失败,再试一次吧~');

    };

    为什么说这个逻辑“基础但关键”?因为我见过很多新手绕开FormData,用

    FileReader读文件成Base64再传——不是不行,但Base64会比原文件大30%,传大文件的时候更卡;而且FormData能同时传文件和其他字段,比Base64灵活多了。比如我朋友的博客头像上传,一开始用Base64,1MB的头像传要2秒,换成FormData后只要1秒不到,还稳定。

    AJAX上传最容易踩的3个坑,我帮你踩过了

    基础逻辑学会了,但实际做的时候,你肯定会遇到一些“奇奇怪怪”的问题——我帮不同客户做过5次AJAX上传功能,踩了3个最常见的坑,今天一次性告诉你怎么避。

    坑1:进度条做不出来?其实是没监听对事件

    我之前帮电商客户做商品图上传时,明明加了进度条代码,但进度条就是不动。查了3小时才发现:进度事件要绑在

    xhr.upload上,不是xhr本身!

    正常的进度监听应该这么写:

    js

    xhr.upload.onprogress = function(e) {

    if (e.lengthComputable) { // 判断是否能获取进度

    let percent = (e.loaded / e.total) 100; // 计算百分比

    document.getElementById(‘progressBar’).style.width = percent + ‘%’; // 更新进度条

    }

    };

    为什么?因为

    xhr本身的progress事件是监听“下载进度”(比如后端返回数据的进度),而xhr.uploadprogress才是“上传进度”——我之前就是绑错了地方,所以进度条没反应。后来改绑到upload上,进度条一下子就动了,客户还夸这个功能“很专业”。

    坑2:大文件上传卡顿?试试分片上传

    上个月帮做短视频的客户做上传功能,1GB的视频直接传,要么超时,要么传到一半断开。后来我想起之前学过的分片上传——把大文件切成一小块一小块的,分多次发给后端,后端再拼起来。

    具体怎么做?核心是

    File.slice()方法——它能把文件切成指定范围的“分片”。比如把1GB的文件切成100MB一块:

  • 计算分片参数
  • const chunkSize = 100 1024 1024; // 100MB每片const totalChunks = Math.ceil(file.size / chunkSize); // 总片数

  • 循环发送分片:用
  • for循环遍历每一片,用file.slice(i chunkSize, (i + 1) chunkSize)切出分片,然后把分片、分片序号、总片数一起发给后端:

    js

    for (let i = 0; i < totalChunks; i++) {

    let chunk = file.slice(i

    chunkSize, (i + 1) chunkSize);

    let formData = new FormData();

    formData.append(‘chunk’, chunk);

    formData.append(‘chunkIndex’, i);

    formData.append(‘totalChunks’, totalChunks);

    formData.append(‘fileName’, file.name);

    // 发送这个formData给后端

    sendChunk(formData); // 这里需要写一个发送分片的函数

    }

  • 后端合并分片:后端收到所有分片后,按chunkIndex的顺序把分片拼起来,就能得到完整的文件。
  • 这样做的好处是稳定——就算某一片传失败了,只需要重新传那一片,不用整个文件重传;而且后端处理小文件也更轻松。那个短视频客户用了分片上传后,1GB的视频上传成功率从50%升到了95%,老板还加了我微信说“下次有需求找你”。

    坑3:跨域报错?不是AJAX的问题,是后端没配置CORS

    我上个月做前端项目时,和后端联调上传功能,老是报“Access-Control-Allow-Origin”错误。我一开始以为是AJAX写得有问题,查了好几遍代码——FormData没错,XHR参数没错,请求地址也没错,后来才发现:跨域是浏览器的同源策略限制,和AJAX没关系,得让后端配置CORS

    什么是CORS?简单说就是后端告诉浏览器:“这个前端域名是安全的,你让它访问我吧”。具体要让后端加3个响应头:

  • Access-Control-Allow-Origin: https://你的前端域名.com(或者
  • 表示允许所有域名,但不推荐,不安全);

  • Access-Control-Allow-Methods: POST, GET, OPTIONS(允许的请求方法,上传一般用POST);
  • Access-Control-Allow-Headers: Content-Type(允许的请求头,FormData会自动加Content-Type: multipart/form-data,所以得允许这个头)。

    要是你传了自定义头信息(比如

    Authorizationtoken),后端还得加Access-Control-Allow-Headers: Authorization。我当时和后端说清楚后,他5分钟就配置好了,跨域问题一下子就解决了——原来之前的报错,根本不是我的问题!

    下面这个表格,是我整理的AJAX上传 vs 传统表单上传的区别,你一看就明白为什么AJAX更好用:

    对比项 传统表单上传 AJAX上传
    页面刷新 是(会丢失表单内容) 否(无刷新体验)
    进度监控 无法实现 可以精准显示上传百分比
    大文件支持 容易超时/卡顿 可分片上传,稳定性高
    用户体验 差(刷新后得重新填内容) 好(实时反馈上传状态)

    你要是按这些方法试了,欢迎回来告诉我效果!要是遇到什么奇怪的问题——比如进度条突然跳满,或者分片合并后文件损坏——评论区留个言,我帮你想想办法——毕竟这些坑我都踩过,说不定能帮你省点时间~


    你是不是经常遇到要传好几个文件的情况?比如做商品详情页要传5-10张不同角度的图,或者传客户案例要传一组照片?其实AJAX完全能搞定多文件上传,步骤跟单文件差不多,就多两步小操作。首先得给文件输入框加个multiple属性——把原来的改成,这样用户选文件时就能像在电脑上选文件一样,按住Ctrl键点选多个,或者拖个框选一片,不用再点好几次“选择文件”。我之前帮朋友做婚纱摄影的案例上传功能,就这么改了一下,用户反馈说“终于不用传一张等一张了”。

    选好多个文件后,JS处理也不复杂。之前单文件是拿e.target.files[0],多文件就是直接拿e.target.files这个数组,里面每个元素都是选好的文件对象。接下来只要循环这个数组,把每个文件加到FormData里就行——比如用forEach遍历,每遍历一个就调用formData.append('file', 当前文件)。这里有个小技巧:FormData的append方法允许同一个键名加多次,不用改成file1file2这种,就用同一个file键名,服务器端收到会自动当成列表处理。比如PHP里用$_FILES['file']能拿到所有文件,Python Django用request.FILES.getlist('file')也能拿到,后端不用改太多代码。我之前用这个方法传过8张1MB左右的商品图,不到2秒就传完了,页面没刷新,用户还能接着填商品名字和描述,体验比传统表单好太多。


    FormData在老浏览器里能用吗?

    FormData的兼容性很好,IE10及以上版本的浏览器都支持,Chrome、Firefox、Safari等现代浏览器也都兼容。如果你的网站不需要兼容IE9及以下,完全可以放心用;如果要兼容更老的浏览器,可能需要用Flash或其他替代方案,但现在大部分场景下FormData已经足够覆盖。

    AJAX上传能同时传多个文件吗?

    可以的。只需要给文件输入框加个multiple属性(比如),用户选文件时就能选多个。然后在JS里循环e.target.files数组,把每个文件都用formData.append('file', 文件对象)加进去就行——服务器端只要按多个文件的逻辑接收就行。

    为什么进度条没反应?

    大概率是进度事件绑错了地方!进度事件要绑定在xhr.upload对象上,而不是xhr本身。因为xhr.upload负责监听上传阶段的进度,xhr本身的进度事件是监听下载阶段的(比如服务器返回数据的进度)。把onprogress事件绑到xhr.upload上,进度条就能正常显示了。

    跨域报错是AJAX代码的问题吗?

    不是的。跨域报错是浏览器的同源策略限制,和AJAX代码没关系,得让后端配置CORS(跨域资源共享)。具体来说,后端需要在响应头里加三个字段:Access-Control-Allow-Origin(允许的前端域名)、Access-Control-Allow-Methods(允许的请求方法,比如POST)、Access-Control-Allow-Headers(允许的请求头,比如Content-Type)。配置好这些,跨域问题就能解决。

    多大的文件需要用分片上传?

    没有固定的数字标准,一般 100MB以上的文件用分片上传。因为大文件直接传容易超时、卡顿,或者因为网络波动导致上传失败——分片上传能把大文件切成小份,就算某一份传失败,只需要重传那一份,不用整个文件重新传,稳定性更高。如果你的文件大部分是几MB的小图,直接传就行,没必要分片。

    原文链接:https://www.mayiym.com/47028.html,转载请注明出处。
  • 0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码