LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

你还不知道的WEB浏览器大文件上传

admin
2025年3月31日 23:51 本文热度 170

说起文件上传,在我们的开发中是绕不过去的话题。但要是碰到几十 MB 甚至 GB 级别的大文件,传统上传方式就有点 “拉胯” 了。今天就给大伙唠唠前端大文件上传,讲讲它的原理、能解决啥问题、有哪些关键功能,再给大家推荐一个超实用的大文件上传库,让大文件上传不再是难题。

一、大文件上传是啥?

(一)大文件上传的定义

简单来说,大文件上传就是把个头很大的文件,从咱们的客户端,像浏览器啥的,传到服务器上。这些大文件要是用传统上传方法,很容易出现上传失败、网络超时,还可能把服务器资源占得死死的。所以,大文件上传一般得靠分片上传、断点续传、并行上传这些技术,才能顺顺利利完成。

(二)大文件上传解决的问题

  1. 网络不稳定:网络这东西,时好时坏。大文件上传的时候,网络要是波动或者断了,很容易就上传失败。大文件上传技术就能应对这种情况,保证上传能完成。
  2. 服务器资源限制:大文件上传要是处理不好,服务器的内存和 CPU 就遭老罪了,搞不好服务器都得崩溃。用对技术,就能减少对服务器资源的占用,让服务器稳稳地运行。
  3. 用户体验:有了上传进度展示、断点续传这些功能,用户能随时知道上传到啥程度了,就算上传中断,也不用从头再来,体验感直接拉满。
  4. 文件完整性验证:文件传输的时候,数据有可能损坏或者丢失。大文件上传技术有文件完整性验证,能保证上传的文件和原来的一模一样。

(三)大文件上传对比普通文件上传的优势

  1. 能处理大文件:普通文件上传碰到 GB 级别的大文件,基本就歇菜了。大文件上传用分片技术,再大的文件都能搞定。
  2. 断点续传:普通文件上传中断了就得重新开始,大文件上传能接着没传完的地方继续,省时间又省带宽。
  3. 并行上传:大文件上传可以把文件分成好多片同时上传,速度 “蹭蹭” 往上涨。
  4. 优化资源利用:分片上传后,服务器每次处理的文件片段小,内存占用就少,服务器性能也能提升。
  5. 精准进度监控:大文件上传能更准确地显示上传进度,用户心里有数。

二、前端实现大文件上传的关键要点

从前端角度看,要实现大文件上传,得搞定下面这些关键功能:

(一)文件分片

把大文件按照固定大小,比如 1MB 或者 5MB,切成一个个小文件,也就是分片。这可以用 File API 里的 File.slice () 方法来实现。

(二)分片上传

用 XMLHttpRequest 或者 Fetch API,把每个分片一个一个传到服务器。为了更快,还能同时上传好几个分片,也就是并行上传。

(三)断点续传

上传要是中断了,前端得记住哪些分片已经传了。下次接着传没传完的。这得前端和服务器一起配合,服务器一般会记录已经上传的分片信息,像文件哈希值或者分片索引。

(四)上传进度监控

通过 XMLHttpRequest 的 progress 事件,或者 Fetch API 的 ReadableStream,实时监控上传进度。然后算出已经上传的百分比,展示给用户。

(五)文件完整性校验

文件上传完了,前端算一下文件的哈希值,比如 MD5 或者 SHA-256,再和服务器那边校验一下,保证文件没出问题。这得用 FileReader 读取文件内容来生成哈希值。

(六)错误处理与重试机制

上传的时候难免会遇到网络错误、服务器错误啥的。前端得有一套完善的错误处理机制,每个分片还要有重试机制,保证上传失败的分片能重新传。

(七)并发控制

要控制好同时上传的分片数量,不然太多请求会占满带宽,服务器也扛不住。可以用队列或者 Promise 池来管理并发上传任务。

(八)用户体验优化

得给用户一个清晰直观的界面反馈,像进度条、上传速度、剩余时间这些都得展示出来。另外,还要支持拖拽上传、文件选择、批量上传这些实用功能。

(九)安全性考虑

为了防止有人上传恶意文件,得校验文件类型,限制文件大小。要是有敏感数据,就用 HTTPS 这些加密方式传输。

三、宝藏大文件上传库 ——enlarge - file - upload

自己从头写一个大文件上传库,难度可不小,要实现这么多功能,短时间内根本搞不定。给大家推荐一个超好用的库,叫 enlarge - file - upload。上面说的这些功能,它都实现了,不管你用 vue2、vue3、react,还是 jquery 原生 js 开发项目,都能直接用,真正做到开箱即用。

下面讲讲它在不同环境里咋用:

(一)安装

npm install enlarge-file-upload

安装很简单,就按常规的 npm 安装方式就行。在项目目录下打开命令行,输入npm install enlarge-file-upload,等安装完,就能在项目里用这个库了。具体安装命令也可以去 npm 官网瞅瞅:www.npmjs.com/package/enl…

(二)参数介绍

这个库的参数挺多,能满足各种项目需求。详细的参数说明可以看官方文档。这里给大家讲讲常用的几个参数:

  • file:这个是要上传的文件对象,必须得有。你选好要上传的文件,获取到的文件对象就填这儿。
  • serverUrl:这是服务器接收文件上传的接口地址,得填对,不然文件传不到服务器上。
  • onProgress:这是个回调函数,用来监听上传进度。上传的时候,它会实时被触发,通过它能拿到当前的上传进度百分比,方便展示给用户看。
  • onSuccess:文件上传成功了,就会执行这个回调函数。你可以在里面给用户提示上传成功,或者更新一下页面状态啥的。
  • onError:要是上传出错了,这个回调函数就会被调用,它会带着错误信息,方便你排查问题。

(三)使用案例

  1. jquery 原生 js 中使用示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文件上传</title>
  </head>
  <script src="https://cdn.jsdelivr.net/npm/enlarge-file-upload/dist/upload.min.js"></script>
  <!-- 引入 Axios 库,用于发送 HTTP 请求 -->
  <script src="https://cdn.jsdelivr.net/npm/axios"></script>

  <body>
    <input type="file" id="fileInput" />
    <button id="pauseButton">暂停上传</button>
    <button id="resumeButton">继续上传</button>
    <div id="progress">上传进度:0%</div>
    <div id="speed">上传速度:0 MB/s</div>
    <script>
      // 定义上传函数
      async function uploadFunction({ chunk, index, hash, cancelToken }) {
        const formData = new FormData();
        formData.append("chunk", chunk);
        formData.append("hash", hash);
        formData.append("index", index);
        await axios.post("http://localhost:3000/api/users", formData, {
          cancelToken,
        });
      }

      // 使用示例
      const config = {
        chunkSize: 5 * 1024 * 1024, // 5MB
        concurrency: 5,
        maxRetries: 3,
        // startOffset: 6, // 从索引为10的切片位置开始传
        // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
        uploadFunction,
        onProgress: (progress) => {
          document.getElementById(
            "progress"
          ).innerText = `上传进度:${state.progress.toFixed(2)}%`;
        },
        onSuccess: () => {
          console.log("上传完毕");
        },
        onSpeed: (speed) => {
          document.getElementById("speed").innerText = `上传速度:${speed}`;
        },
      };

      const { upload, pause, resume, state } = createUploader(config);
      const fileInput = document.getElementById("fileInput");
      fileInput.addEventListener("change", () => {
        const file = fileInput.files[0];
        upload(file);
      });
      // 暂停上传
      document.getElementById("pauseButton").addEventListener("click", () => {
        pause();
      });
      // 继续上传
      document.getElementById("resumeButton").addEventListener("click", () => {
        resume();
      });
    </script>
  </body>
</html>

</html>

在这个例子里,先引入了jquery库和enlarge-file-upload库。页面加载完后,给上传按钮绑定了点击事件。点按钮的时候,就去获取文件输入框里选的文件。要是有文件,就创建EnlargeFileUpload实例,把文件对象、服务器上传接口地址,还有各种回调函数传进去,最后调用start方法开始上传文件。

  1. 在 vue3 中使用
<template>
    <div>
        <input type="file" @change="handleFileChange">
        <button @click="uploadFile">上传文件</button>
    </div>
</template>

<script setup>
import EnlargeFileUpload from 'enlarge-file-upload';

const fileRef = ref(null);
const handleFileChange = (e) => {
    fileRef.value = e.target.files[0];
};

const uploadFile = () => {
    if (fileRef.value) {
        const uploader = new EnlargeFileUpload({
            file: fileRef.value,
            serverUrl: 'http://your-server-url.com/upload',
            onProgress: (progress) => {
                console.log(`上传进度: ${progress}%`);
            },
            onSuccess: () => {
                console.log('文件上传成功');
            },
            onError: (error) => {
                console.error('文件上传失败', error);
            }
        });
        uploader.start();
    }
};
</script>

在 vue3 项目里,用ref定义了一个fileRef来存选的文件。文件选择框内容变了,handleFileChange函数就把选的文件赋值给fileRef。点上传按钮的时候,看看fileRef有没有值,有值就创建EnlargeFileUpload实例开始上传文件,还设置了上传进度、成功和失败的回调函数。

  1. 在 vue2 中使用
<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <button @click="handlePause">暂停上传</button>
    <button @click="handleResume">继续上传</button>
    <div>上传进度:{{ progress.toFixed(2) }}%</div>
    <div>上传速度:{{ speed }}</div>
  </div>
</template>

<script>
import Vue from "vue";
import createUploader from "enlarge-file-upload";
import axios from "axios";

export default Vue.extend({
  data() {
    return {
      progress: 0, // 进度
      speed: "0 MB/s", // 速度
      uploader: null, // 上传器实例
    };
  },
  methods: {
    // 定义上传函数
    async uploadFunction({ chunk, index, hash, cancelToken }) {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("hash", hash);
      formData.append("index", index.toString());
      await axios.post("http://localhost:3000/api/users", formData, {
        cancelToken,
      });
    },

    // 文件选择处理
    handleFileChange(event) {
      const file = event.target.files?.[0];
      if (file && this.uploader) {
        this.uploader.upload(file);
      }
    },

    // 暂停上传
    handlePause() {
      if (this.uploader) {
        this.uploader.pause();
      }
    },

    // 继续上传
    handleResume() {
      if (this.uploader) {
        this.uploader.resume();
      }
    },
  },
  created() {
    const uploaderConfig = {
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      uploadFunction: this.uploadFunction,
      onProgress: (progressValue) => {
        this.progress = progressValue;
      },
      onSuccess: () => {
        console.log("上传完毕");
      },
      onSpeed: (speedValue) => {
        this.speed = speedValue;
      },
    };

    // 创建上传器实例
    this.uploader = createUploader(uploaderConfig);
  },
});
</script>

<style scoped></style>

vue2 项目里,在data里定义了file变量存文件。handleFileChange方法获取选的文件,点上传按钮的时候,要是file有值,就创建EnlargeFileUpload实例上传文件,设置好回调函数处理上传过程里的各种情况。

  1. React 中使用示例
import React, { useRef, useMemo } from'react';
import EnlargeFileUpload from 'enlarge-file-upload';

const FileUploadComponent = () => {
    const fileInputRef = useRef(null);

    const uploadFile = () => {
        const file = fileInputRef.current.files[0];
        if (file) {
            const uploader = useMemo(() => new EnlargeFileUpload({
                file: file,
                serverUrl: 'http://your-server-url.com/upload',
                onProgress: (progress) => {
                    console.log(`上传进度: ${progress}%`);
                },
                onSuccess: () => {
                    console.log('文件上传成功');
                },
                onError: (error) => {
                    console.error('文件上传失败', error);
                }
            }), [file]);
            uploader.start();
        }
    };

    return (
        <div>
            <input type="file" ref={fileInputRef} />
            <button onClick={uploadFile}>上传文件</button>
        </div>
    );
};

export default FileUploadComponent;

React 项目里,用useRef创建了fileInputRef来引用文件输入框。点上传按钮的时候,从fileInputRef里拿选的文件。为了防止组件重新渲染的时候重复创建EnlargeFileUpload实例,用useMemo来创建上传实例,设置好回调函数。最后在页面里渲染文件输入框和上传按钮。

  1. 封装为 react Hooks
import React, { useState, useMemo } from "react";
import createUploader from "enlarge-file-upload";
import type { Config, UploadOptions } from "enlarge-file-upload";
import axios from "axios";

const FileUpload = () => {
  const [progress, setProgress] = useState(0);
  const [speed, setSpeed] = useState("0 MB/s");

  // 定义上传函数
  async function uploadFunction({
    chunk,
    index,
    hash,
    cancelToken,
  }: UploadOptions) {
    const formData = new FormData();
    formData.append("chunk", chunk);
    formData.append("hash", hash);
    formData.append("index", index);
    await axios.post("http://localhost:3000/api/users", formData, {
      cancelToken,
    });
  }

  const uploaderConfig: Config = useMemo(
    () => ({
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      // startOffset: 6, // 从索引为10的切片位置开始传
      // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
      uploadFunction,
      onProgress: (progress) => {
        setProgress(progress);
      },
      onSuccess: () => {
        console.log("上传完毕");
      },
      onSpeed: (speed) => {
        setSpeed(speed);
      },
    }),
    []
  );

  const uploader = useMemo(
    () => createUploader(uploaderConfig),
    [uploaderConfig]
  );

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    uploader?.upload(file);
  };

  const handlePause = () => {
    uploader?.pause();
  };

  const handleResume = () => {
    uploader?.resume();
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handlePause}>暂停上传</button>
      <button onClick={handleResume}>继续上传</button>
      <div>上传进度:{progress.toFixed(2)}%</div>
      <div>上传速度:{speed}</div>
    </div>
  );
};

export default FileUpload;

这段代码把文件上传功能封装成了 React Hook。Hook 里用useRef创建文件输入框的引用,uploadFile函数处理文件上传逻辑,最后返回fileInputRefuploadFile,方便在其他组件里复用文件上传功能。

  1. 使用上面封装好的 Hooks 示例
import { useState, useMemo, useCallback } from "react";
import createUploader from "enlarge-file-upload";
import type { Config, UploadOptions } from "enlarge-file-upload";
import axios from "axios";

const useFileUploader = () => {
  const [progress, setProgress] = useState(0);
  const [speed, setSpeed] = useState("0 MB/s");

  const uploadFunction = useCallback(
    async ({ chunk, index, hash, cancelToken }: UploadOptions) => {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("hash", hash);
      formData.append("index", index);
      await axios.post("http://localhost:3000/api/users", formData, {
        cancelToken,
      });
    },
    []
  );

  const uploaderConfig: Config = useMemo(
    () => ({
      chunkSize: 5 * 1024 * 1024, // 5MB
      concurrency: 5,
      maxRetries: 3,
      // startOffset: 6, // 从索引为10的切片位置开始传
      // includeChunks:[1,6], // 只上传索引为1和6的切片,只有startOffset为0或空时才生效
      uploadFunction,
      onProgress: (progress) => {
        setProgress(progress);
      },
      onSuccess: () => {
        console.log("Upload complete");
      },
      onSpeed: (speed) => {
        setSpeed(speed);
      },
    }),
    [uploadFunction]
  );

  const uploader = useMemo(
    () => createUploader(uploaderConfig),
    [uploaderConfig]
  );

  const uploadFile = useCallback(
    (file) => {
      uploader?.upload(file);
    },
    [uploader]
  );

  const pauseUpload = useCallback(() => {
    uploader?.pause();
  }, [uploader]);

  const resumeUpload = useCallback(() => {
    uploader?.resume();
  }, [uploader]);

  return {
    progress,
    speed,
    uploadFile,
    pauseUpload,
    resumeUpload,
  };
};

export default useFileUploader;

这个示例里,引入封装好的useFileUpload Hook,通过解构拿到fileInputRefuploadFile。在组件里用fileInputRef关联文件输入框,点按钮就调用uploadFile函数上传文件,在 React 项目里用起来就更方便了。

大文件上传在前端开发里很重要,搞懂原理和实现方法,再用好工具,就能给用户更好的上传体验。希望这篇文章能帮大家深入了解前端大文件上传,在项目开发里用得得心应手。


作者:小蚂蚁i
链接:https://juejin.cn/post/7474923858165137417
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

该文章在 2025/4/1 12:54:00 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved