首页 > web前端 > js教程 > 正文

js如何实现文件上传

小老鼠
发布: 2025-08-19 14:02:01
原创
961人浏览过

文件上传的核心是通过input[type="file"]获取文件,使用formdata封装,再通过xhr或fetch发送;2. 进度显示依赖xhr的upload.onprogress事件,取消上传可通过调用abort()方法实现;3. 前端校验文件类型可检查file.type,校验大小可比较file.size,但必须配合后端校验以确保安全;4. 常见问题包括跨域需后端配置cors、后端处理文件存储与命名、提升用户体验需明确错误提示、大文件应采用分片上传、安全性必须由后端进行文件类型、大小、病毒扫描和权限控制,前端校验仅为辅助,完整的文件上传需前后端协同完成。

js如何实现文件上传

JavaScript实现文件上传,核心其实就是利用HTML5的

input type="file"
登录后复制
元素获取用户选择的文件,然后通过
FormData
登录后复制
对象把文件数据封装起来,最后用
XMLHttpRequest
登录后复制
(XHR)或者更现代的
fetch
登录后复制
API将这些数据发送到服务器。说白了,就是前端把文件“打包”好,再通过HTTP请求“寄”给后端。

解决方案

文件上传的实现步骤,通常会是这样一套组合拳:

首先,HTML部分需要一个文件输入框和一个触发上传的按钮,或者直接监听文件输入框的

change
登录后复制
事件。

<input type="file" id="fileInput" multiple> <!-- 允许选择多个文件 -->
<button id="uploadButton">上传文件</button>
<div id="status"></div>
登录后复制

接着是JavaScript的逻辑,这块儿才是真正的核心:

document.addEventListener('DOMContentLoaded', () => {
    const fileInput = document.getElementById('fileInput');
    const uploadButton = document.getElementById('uploadButton');
    const statusDiv = document.getElementById('status');

    uploadButton.addEventListener('click', () => {
        // 检查是否有文件被选中
        if (fileInput.files.length === 0) {
            statusDiv.textContent = '请先选择文件。';
            return;
        }

        const formData = new FormData();
        // 遍历所有选中的文件,添加到FormData对象中
        // 注意:这里的'myFile'是后端接收文件时使用的字段名,可以自定义
        for (let i = 0; i < fileInput.files.length; i++) {
            formData.append('myFile', fileInput.files[i]);
        }

        // 创建XMLHttpRequest对象
        const xhr = new XMLHttpRequest();

        // 配置请求:POST方法,目标URL
        // 实际开发中,这个URL会指向你的后端文件上传接口
        xhr.open('POST', '/upload', true); // true表示异步请求

        // 监听上传进度
        xhr.upload.onprogress = (event) => {
            if (event.lengthComputable) {
                const percentComplete = (event.loaded / event.total) * 100;
                statusDiv.textContent = `上传中: ${percentComplete.toFixed(2)}%`;
            }
        };

        // 监听请求完成或失败
        xhr.onload = () => {
            if (xhr.status === 200) {
                statusDiv.textContent = '文件上传成功!' + xhr.responseText;
                // 上传成功后,通常会清空文件选择,防止重复提交
                fileInput.value = '';
            } else {
                statusDiv.textContent = `文件上传失败: ${xhr.status} ${xhr.statusText}`;
            }
        };

        // 监听请求错误
        xhr.onerror = () => {
            statusDiv.textContent = '网络错误或服务器无响应。';
        };

        // 发送FormData数据
        xhr.send(formData);
    });
});
登录后复制

上面这段代码,我个人觉得已经把文件上传的最基本流程说清楚了。它利用了

FormData
登录后复制
这个专门处理表单数据的接口,能很好地处理文件这种二进制数据。而
XMLHttpRequest
登录后复制
虽然有点老,但在文件上传这种场景下,它的
xhr.upload
登录后复制
对象能提供非常精细的进度控制,这是
fetch
登录后复制
API目前还不太方便直接做到的地方。

如何实现文件上传的进度显示和取消功能?

文件上传过程中,用户最关心的莫过于上传到哪一步了,有没有卡住,能不能取消。实现这些功能,其实都离不开

XMLHttpRequest
登录后复制
对象的一些特定事件和方法。

进度显示,我们主要依赖

xhr.upload.onprogress
登录后复制
事件。这个事件会在文件上传过程中周期性触发,每次触发时,事件对象
event
登录后复制
里会包含
loaded
登录后复制
(已上传字节数)和
total
登录后复制
(总字节数)。有了这两个值,计算百分比就轻而易举了。

// ... 在上面的xhr.open()和xhr.send()之间加入
xhr.upload.onprogress = (event) => {
    if (event.lengthComputable) { // 确保总长度是可计算的
        const percentComplete = (event.loaded / event.total) * 100;
        document.getElementById('status').textContent = `上传中: ${percentComplete.toFixed(2)}%`;
        // 你还可以更新一个进度条元素的宽度等
        // document.getElementById('progressBar').style.width = percentComplete + '%';
    }
};
登录后复制

至于取消上传,

XMLHttpRequest
登录后复制
提供了一个非常直接的方法:
abort()
登录后复制
。你只需要在某个时机(比如用户点击了“取消”按钮)调用这个方法,当前的上传请求就会被中止。

Cutout老照片上色
Cutout老照片上色

Cutout.Pro推出的黑白图片上色

Cutout老照片上色 20
查看详情 Cutout老照片上色
<button id="cancelUpload">取消上传</button>
登录后复制
// ... 在DOMContentLoaded事件监听器内
const cancelUploadButton = document.getElementById('cancelUpload');
let currentXHR = null; // 用于存储当前的XHR实例,以便取消

uploadButton.addEventListener('click', () => {
    // ... 前面的文件检查和FormData创建逻辑

    const xhr = new XMLHttpRequest();
    currentXHR = xhr; // 保存当前的XHR实例

    // ... xhr.open(), xhr.upload.onprogress, xhr.onload, xhr.onerror

    xhr.send(formData);
});

cancelUploadButton.addEventListener('click', () => {
    if (currentXHR) {
        currentXHR.abort(); // 中止请求
        document.getElementById('status').textContent = '上传已取消。';
        currentXHR = null; // 清除引用
    }
});
登录后复制

这里有个小细节,我通常会用一个变量

currentXHR
登录后复制
来保存当前的
XMLHttpRequest
登录后复制
实例。这样,当用户点击取消时,就能准确地找到并中止正在进行的那个上传任务。如果前端页面允许同时发起多个上传,那这个管理方式就需要更复杂一些,比如用一个Map来存储每个文件的XHR实例。

前端如何进行文件类型和大小的校验?

在文件上传到服务器之前,前端进行一些基本的校验是非常有必要的。这能有效提升用户体验,避免用户上传了不符合要求的文件后才发现错误,同时也能减轻服务器的压力。当然,我得强调,前端校验只是第一道防线,服务器端校验才是最终且不可或缺的安全保障。

文件类型校验,我们可以通过

File
登录后复制
对象的
type
登录后复制
属性来判断。这个属性会返回文件的MIME类型(例如
image/jpeg
登录后复制
,
application/pdf
登录后复制
)。

// ... 在FormData创建之前
for (let i = 0; i < fileInput.files.length; i++) {
    const file = fileInput.files[i];

    // 检查文件类型
    const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
    if (!allowedTypes.includes(file.type)) {
        statusDiv.textContent = `文件 "${file.name}" 的类型不被允许。只允许 JPG, PNG, PDF。`;
        fileInput.value = ''; // 清空文件选择
        return; // 阻止上传
    }
    // ... 添加到formData
    formData.append('myFile', file);
}
登录后复制

需要注意的是,

file.type
登录后复制
浏览器根据文件内容或者文件扩展名推断出来的,用户如果恶意修改文件扩展名,这个值可能就不准确了。所以,这只是个初步的筛选。

文件大小校验则更直接,

File
登录后复制
对象的
size
登录后复制
属性会返回文件的大小,单位是字节。

// ... 在FormData创建之前
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB

for (let i = 0; i < fileInput.files.length; i++) {
    const file = fileInput.files[i];

    // 检查文件大小
    if (file.size > MAX_FILE_SIZE) {
        statusDiv.textContent = `文件 "${file.name}" 太大,最大允许 ${MAX_FILE_SIZE / (1024 * 1024)}MB。`;
        fileInput.value = '';
        return;
    }
    // ... 添加到formData
    formData.append('myFile', file);
}
登录后复制

将类型和大小校验放在一起,可以在文件被添加到

FormData
登录后复制
之前就进行判断,不符合条件就直接阻止上传,给用户一个即时的反馈。这种体验,远比上传到一半或者上传完了才发现错误要好得多。

在文件上传中,有哪些常见的坑或者需要注意的问题?

文件上传这事儿,看起来简单,但实际操作起来,总会遇到一些让人头疼的问题。这里我总结一些自己踩过的“坑”或者值得注意的地方:

1. 跨域问题 (CORS): 这是最常见的。如果你的前端应用和后端文件上传接口不在同一个域(协议、域名、端口任一不同),那么浏览器会因为同源策略而阻止你的XHR请求。解决办法主要在后端:后端服务器需要设置正确的CORS响应头,比如

Access-Control-Allow-Origin
登录后复制
来允许你的前端域访问。前端通常不需要额外配置,但知道原理很重要。

2. 后端处理的复杂性: 前端把文件发出去了,后端接收后才是真正的挑战。后端需要处理文件存储(存到哪里?本地磁盘?云存储?)、文件名冲突(同名文件怎么处理?加时间戳?UUID?)、文件安全扫描(防止上传恶意文件)、数据库记录(文件路径、大小、上传者等信息)。这些都不是前端能管的,但作为前端开发者,理解这些能更好地与后端协作。

3. 用户体验与反馈: 上传失败了,是网络问题?文件太大?服务器错误?给用户一个清晰、友好的错误提示至关重要。我见过很多网站,上传失败就一个“上传失败”了事,用户一脸懵。此外,上传成功后,是否需要展示文件预览?是否需要提供删除功能?这些都是提升用户体验的细节。网络不好的情况下,如果能有断点续传或者重试机制,那就更棒了(虽然实现起来复杂不少)。

4. 大文件上传的性能考量: 当文件非常大时(比如几百MB甚至GB),一次性上传可能会导致网络拥堵、内存溢出,甚至服务器超时。这时候,通常需要考虑分片上传(Chunked Upload)。前端将大文件切割成小块,逐个上传,后端接收后再拼接起来。这不仅能提高上传成功率,还能实现断点续传。虽然复杂,但对于大文件是必选项。

5. 安全性: 我前面强调过,前端校验只是辅助。真正的安全校验必须在服务器端进行。这包括但不限于:

  • 文件类型校验: 仅通过文件扩展名或MIME类型是不够的,后端可能需要读取文件头来判断真实类型。
  • 文件大小校验: 再次确认文件大小,防止恶意上传超大文件耗尽资源。
  • 病毒扫描: 对上传的文件进行病毒扫描,防止恶意软件。
  • 权限校验: 确保只有授权用户才能上传文件,并且只能上传到其有权限的目录。

总的来说,文件上传是个系统工程,前端负责“送货”,后端负责“收货”和“保管”。双方紧密配合,才能构建一个健壮、安全、用户体验良好的文件上传系统。

以上就是js如何实现文件上传的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号