前端文件上传实现代码示例:分片、断点续传与秒传

作者:小满时间:2026-10-31 15:59:23浏览:558

本文详细介绍前端文件上传的实现方案,涵盖普通上传、分片上传、断点续传及秒传功能。通过封装 Axios 请求,利用 FormData 传输文件,并结合 MD5 计算与分片策略解决大文件传输问题。文中提供了处理网络中断、页面刷新及切片失败等异常场景的具体思路与代码示例,帮助开发者构建稳定高效的前端上传模块。

常规文件上传

方案:

第一步,提取用户选取的文件实体,并追加至 FormData 实例。随后,调用 axios 的 post 接口将 FormData 实体传输至服务端。在 then 与 catch 回调中,分别应对上传成功及异常场景,并打印对应日志。

关键约束:采用 axios 传输文件时,务必指定数据格式为 multipart/form-data,否则文件实体无法正确送达。

传统方案:

function handleFileSelect(e) {
    const formData = new FormData();
    formData.append("file", file);
    const header={"Content-Type": "multipart/form-data;charset=UTF-8"};
    axios.post("http://xxx.xxx.xx.x:xxxx/upload",formData,{
       headers: header,
    }).then((res) => {
       console.log(res);
    });
}

方法封装:

在大型工程架构中,通常对 get、post、put、delete 及 upload 等接口进行统一封装。

//封装upload方法
import axios from "axios";
const requests = axios.create({
  //配置对象
  baseURL: myBaseURL,
  timeout: 10000,
});
const header = {
  "Content-Type": "multipart/form-data;charset=UTF-8",
};
const http = {
  upload(url="",formData){
    return new Promise((resolve, reject) => {
      requests({
        url,
        data: formData,
        headers: header,
        method: "POST",
      })
        .then((res) => {
          resolve(res.data);
          return res;
        })
        .catch((err) => {
          reject(err);
        });
    });
  },
....
}
//封装请求方法
apiFun.upload = (formData) => {
  return http.upload("/user/headshot", formData);
};
//上传文件
function handleFileSelect(e) {
    const formData = new FormData();
    formData.append("file", file);
    ApiFun.upload(formData).then((res) => {
      console.log(res);
      ElMessage.success("上传成功");
    });
  }
}

分片上传

分片上传机制指将大文件切割为多个子块,并逐一上传至服务器的策略。

方案:

核心流程包含三步:分片=》并行/串行发送分片=》合并请求

实施分片上传的主要动因通常源于文件体积过大。

分片上传旨在解决以下问题:

1. 规避大文件网络传输中断风险:上传大文件时,若网络波动或发生异常,需全量重传。分片机制下,仅需重传失败子块,无需重传整体。

2. 缓解服务器负载:直接上传大文件需服务器分配大量内存处理数据。分片上传可将负载分散至各子块处理,降低服务器压力。

3. 提升上传速率与稳定性:大文件切分后可并行传输,加速上传。单块失败仅影响该块重试,不影响其他块,增强稳定性。

4. 支持断点续传:服务器可记录各子块上传状态(如已传字节数、已确认块)。中断后可从断点继续,实现续传。

具体实施逻辑:

  • 将大文件转换为二进制流格式
  • 利用流的切割特性,将二进制流分割为多份
  • 组装与分割块数量或大小相等的请求块,以并行或串行方式发起请求
  • 待监听所有请求发送成功后,向服务端发送合并指令

通常设定文件小于5MB时采用常规上传,大于5MB时启用分片上传。

常规文件上传

常规文件上传

整体逻辑如下:

常规文件上传

执行大文件分片:

此处封装了 md5 计算逻辑

'use strict';
import '../plugins/js-spark-md5.js'

export default function (file, callback) {
  var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
    file = file,
    chunkSize = 4194304,                             // Read in chunks of 4MB 即 4 * 1024 * 1024
    chunks = Math.ceil(file.size / chunkSize),
    currentChunk = 0,
    spark = new SparkMD5.ArrayBuffer(),              //向上取整,因为最后一块不一定满4MB
    fileReader = new FileReader();

  fileReader.onload = function (e) {
    console.log('read chunk nr', currentChunk + 1, 'of', chunks);
    spark.append(e.target.result);                   // Append array buffer
    currentChunk++;

    if (currentChunk < chunks) {
      loadNext();
    } else {
      let data = {
        "etag": spark.end(),
        "chunks": chunks,
        "size": file.size,
        "blockToken": "",
      }
      callback(null, data);
      console.log('finished loading');
    }
  };

  fileReader.onerror = function () {
    callback('oops, something went wrong.');
  };

  function loadNext() {
    var start = currentChunk * chunkSize,
      end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
  }

  loadNext();
};

因实现了多文件并行分片上传,在将文件加入列表时已计算相关属性

md5 计算完成后,文件显示准备就绪。

常规文件上传

构建切片请求:

常规文件上传

返回结果中的 exist 属性反映文件是否已上传,若存在则支持秒传,无需重复上传。

若不存在,则获取此次上传的目标 IP 及端口号

常规文件上传

以并行/串行方式发出每个切片:

此处使用 promise 实现并行(若采用循环遍历切片串行传输,可通过判断循环下标 i 与切片总数相等来判定分片是否上传完毕)

且每个分片大小一致。

常规文件上传

所有分片上传完成后,向服务器端发送合并请求。

文件合并请求:

常规文件上传

常规文件上传

断点续传

在分片上传基础上,自然衍生出断点续传需求。

方案:

点击暂停按钮时停止上传。点击继续上传,继续上传剩下的分片或请求。

直观思路是为每个分片设置是否上传的状态标识,以识别该分片是否完成。

方法1:初始时所有分片状态为未上传,当一个分片上传完成,将该标识设置为已上传。暂停上传时,将当前上传的分片也设置为未上传;重新进行上传时,就可以继续把其他未上传的分片上传了。

方法2:将所有分片加入一个列表中,每当成功上传一个分片,就将该分片从列表中删除。而列表中剩下的分片就是还未上传的分片。

秒传

即前文分片上传中提及。发送创建分片请求时,让服务器检查该文件是否上传过,如果是,则无需重复上传,直接显示上传成功实现秒传功能。

其他问题 之 上传过程中刷新页面怎么办

若在上传过程中刷新页面,通常会导致上传任务中断。

因为刷新页面会导致浏览器重新加载页面,之前的 JavaScript 代码和网络请求都会被取消。

当页面刷新后,可以尝试使用以下方法来处理上传任务的中断情况:

1. 利用浏览器的缓存机制:在上传之前,将文件对象保存到浏览器的本地存储或会话存储中。当页面刷新后,通过读取缓存中的文件对象信息,重新构建上传任务,并恢复之前的上传进度。

2. 检测页面刷新事件:可以通过监听 `beforeunload` 事件来捕获页面刷新的操作。在该事件触发时,可以弹出一个确认框,提示用户是否继续离开页面。如果用户选择离开页面,可以先中止当前的上传请求,然后再进行页面刷新。

该方法并不能完全保证上传任务的连续性和完整性。在实际应用中,为了确保上传任务的可靠性,通常建议在上传过程中避免刷新页面,或者提供其他手段来处理上传中断的情况,如支持断点续传功能。

使用 localStorage 实现断点续传的demo:

const input = document.getElementById('file-input');
const FILE_STORAGE_KEY = 'uploadedFile';

// 读取本地存储的文件对象信息
const storedFile = localStorage.getItem(FILE_STORAGE_KEY);
let file = null;

if (storedFile) {
  // 如果存在已上传的文件信息,则恢复上传任务
  file = JSON.parse(storedFile);
}

input.addEventListener('change', function() {
  file = input.files[0];
  // 将文件对象保存到本地存储
  localStorage.setItem(FILE_STORAGE_KEY, JSON.stringify(file));
  startUpload();
});

function startUpload() {
  if (!file) {
    console.log('请先选择文件');
    return;
  }

  const formData = new FormData();
  formData.append('file', file);

  axios.post('/upload', formData, {
    onUploadProgress: function(progressEvent) {
      // 处理上传进度变化
      if (progressEvent.lengthComputable) {
        const percentComplete = progressEvent.loaded / progressEvent.total * 100;
        console.log(percentComplete.toFixed(2) + '% 已上传');
      }
    }
  }).then(function(response) {
    // 处理上传完成事件
    console.log('上传完成');
    console.log(response.data);
    
    // 上传完成后,清除本地存储的文件信息
    localStorage.removeItem(FILE_STORAGE_KEY);
  }).catch(function(error) {
    // 处理上传错误事件
    console.error('上传出错');
  });
}

// 监听页面刷新事件
window.addEventListener('beforeunload', function(event) {
  if (file) {
    // 中止当前的上传请求
    // ...
    
    // 移除本地存储的文件信息
    localStorage.removeItem(FILE_STORAGE_KEY);
  }
});

使用 localStorage 存储已选择的文件对象信息,并在页面刷新时恢复该信息。当用户重新选择文件时,更新文件对象并保存到 localStorage 中。在上传过程中,如果用户刷新页面,会触发 beforeunload 事件,我们可以在该事件中中止上传请求并移除 localStorage 中的文件信息。

其他问题 之 某个切片上传失败怎么办

1. 重新上传该切片:如果上传失败的切片是由于网络等原因导致的,则可以尝试重新上传该切片。如果上传失败的切片数量较少,则可以通过手动重试的方式来完成。如果上传失败的切片数量较多,则可能需要设计一些自动化机制来处理重传逻辑,如使用队列等数据结构来记录上传失败的切片并进行重传。

2. 跳过该切片:如果上传失败的切片数量较多,或者由于某些原因无法进行重传,则可以考虑跳过该切片。具体实现方法可以根据上传任务的特点来确定,如将上传任务分为多个阶段,每个阶段上传一定数量的切片,如果某个阶段上传失败,则跳过该阶段并记录下失败的切片信息,待后续再进行重传。

3. 放弃上传任务:如果上传失败的切片数量较多,或者重传操作多次仍然无法恢复上传任务,则可以考虑放弃上传任务。在此情况下,可以将上传任务标记为“失败”状态,并记录下失败的切片信息。如果需要重新上传该文件,则可以在下一次上传任务中,首先检查之前已上传的切片信息,如果存在已上传的切片,则可以直接跳过这些切片并进行后续的上传操作。

总结

到此这篇关于前端文件上传实现的文章就介绍到这了,更多相关前端文件上传内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章