问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

文件上传黑科技:切片、断点续传与秒传技术详解

创作时间:
作者:
@小白创作中心

文件上传黑科技:切片、断点续传与秒传技术详解

引用
CSDN
13
来源
1.
https://blog.csdn.net/z_344791576/article/details/138647507
2.
https://blog.csdn.net/weixin_45525177/article/details/139007431
3.
https://blog.csdn.net/QAZ412803/article/details/144258111
4.
https://blog.csdn.net/gitblog_00018/article/details/139057526
5.
https://blog.csdn.net/m0_62742402/article/details/135815154
6.
https://eyun.baidu.com/content/100458/
7.
https://www.cnblogs.com/Jcloud/p/18007834
8.
https://www.showapi.com/news/article/66ff5d654ddd79f11a30c331
9.
https://juejin.cn/post/7385098943942934582
10.
https://cloud.tencent.com/developer/article/2391382
11.
https://my.oschina.net/emacs_8624026/blog/16829269
12.
https://juejin.cn/post/7436026758438453274
13.
https://juejin.cn/post/7360980866796306442

在当今的Web应用中,文件上传是一个常见的功能需求。然而,随着文件大小的不断增加,传统的上传方式已经难以满足用户的需求。本文将为您介绍几种提升文件上传速度和稳定性的"黑科技",包括文件切片、断点续传和秒传技术。

01

传统文件上传的局限性

传统的文件上传方式通常采用单个HTTP请求完成整个文件的传输。这种方式在处理小文件时表现良好,但当遇到大文件(如视频、大型文档等)时,就会暴露出以下问题:

  1. 上传速度慢:大文件需要占用较长的网络传输时间,尤其是在带宽有限的情况下。
  2. 易中断:网络不稳定或中断会导致整个上传过程失败,需要重新开始。
  3. 用户体验差:长时间的等待和不确定的上传进度会影响用户的使用体验。

为了解决这些问题,现代Web应用引入了多种优化技术。

02

核心技术介绍

文件切片

文件切片是将大文件分割成多个小块(通常称为"切片"或"分片"),然后分别上传。这种方式有以下优点:

  • 提升上传速度:可以利用多线程同时上传多个切片,充分利用网络带宽。
  • 降低中断风险:即使某个切片上传失败,也只需重新上传该切片,而不是整个文件。
  • 支持断点续传:可以记录已上传的切片,在网络中断后继续上传未完成的部分。

断点续传

断点续传技术允许在上传过程中断后,从上次中断的位置继续上传,而不需要重新开始。这对于大文件上传尤为重要,特别是在网络不稳定的情况下。

秒传

秒传技术通过计算文件的哈希值(如MD5)来判断服务器上是否已存在相同文件。如果存在,则无需再次上传,直接返回一个新地址。这种方式可以显著减少上传时间和服务器存储需求。

03

前端实现细节

Web Worker的使用

在前端实现中,Web Worker是一个关键的技术。它允许JavaScript在后台线程中运行,不会阻塞UI线程,从而避免页面卡顿。在大文件上传中,可以使用Web Worker来处理复杂的计算任务,如文件切片和哈希值计算。

// 创建Web Worker
const worker = new Worker('worker.js');

// 在worker.js中处理文件切片
self.onmessage = function(e) {
  const file = e.data.file;
  const chunkSize = 1 * 1024 * 1024; // 每片1MB
  const chunks = Math.ceil(file.size / chunkSize);
  const spark = new sparkMd5.ArrayBuffer();
  const fileReader = new FileReader();

  let currentChunk = 0;
  const loadNext = () => {
    const start = currentChunk * chunkSize;
    const end = ((currentChunk + 1) * chunkSize) >= file.size ? file.size : (currentChunk + 1) * chunkSize;
    fileReader.readAsArrayBuffer(file.slice(start, end));
  };

  fileReader.onload = function(e) {
    spark.append(e.target.result);
    currentChunk++;
    if (currentChunk < chunks) {
      loadNext();
    } else {
      const hash = spark.end();
      self.postMessage({ hash });
    }
  };

  loadNext();
};

axios请求

使用axios可以方便地发送HTTP请求,并且支持上传进度的监控。

const uploadFile = (file, chunkSize) => {
  const chunks = Math.ceil(file.size / chunkSize);
  const promises = [];

  for (let i = 0; i < chunks; i++) {
    const start = i * chunkSize;
    const end = ((i + 1) * chunkSize) >= file.size ? file.size : (i + 1) * chunkSize;
    const chunk = file.slice(start, end);

    const formData = new FormData();
    formData.append('file', chunk);
    formData.append('chunkNumber', i);
    formData.append('totalChunks', chunks);

    const promise = axios.post('/upload', formData, {
      onUploadProgress: (progressEvent) => {
        const progress = (progressEvent.loaded / progressEvent.total) * 100;
        console.log(`Chunk ${i + 1} upload progress: ${progress}%`);
      }
    });

    promises.push(promise);
  }

  return Promise.all(promises);
};

进度条计算

为了准确显示上传进度,需要根据已上传的切片数来计算,而不是仅仅依赖于单个切片的上传进度。

let uploadedChunks = 0;
const totalChunks = chunks.length;

const updateProgress = () => {
  const progress = (uploadedChunks / totalChunks) * 100;
  console.log(`Overall upload progress: ${progress}%`);
};

// 在每个切片上传成功后调用
axios.post('/upload', formData).then(() => {
  uploadedChunks++;
  updateProgress();
});
04

后端处理要点

后端需要实现以下功能:

  1. 切片接收与存储:接收前端上传的每个切片,并将其存储在临时目录中。
  2. 切片合并:当所有切片上传完成后,将它们合并成原始文件。
  3. 哈希值校验:支持秒传功能,通过计算文件哈希值判断是否已存在相同文件。

以下是一个简单的Node.js后端实现示例:

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  const { chunkNumber, totalChunks, identifier } = req.body;
  const filePath = path.join('uploads', `${identifier}_${chunkNumber}`);

  fs.renameSync(req.file.path, filePath);
  res.status(200).send('Chunk uploaded successfully');
});

app.post('/merge', (req, res) => {
  const { identifier, totalChunks } = req.body;
  const writeStream = fs.createWriteStream(path.join('uploads', identifier));

  for (let i = 0; i < totalChunks; i++) {
    const readStream = fs.createReadStream(path.join('uploads', `${identifier}_${i}`));
    readStream.pipe(writeStream, { end: false });

    readStream.on('end', () => {
      fs.unlinkSync(path.join('uploads', `${identifier}_${i}`));
    });
  }

  writeStream.on('finish', () => {
    res.status(200).send('File merged successfully');
  });
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
05

实际应用案例

在实际项目中,可以结合Vue.js框架来实现更友好的用户界面。以下是一个基于Vue2的简单示例:

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <button @click="uploadFile">Upload</button>
    <div>
      Upload Progress: {{ progress }}%
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      file: null,
      progress: 0,
    };
  },
  methods: {
    handleFileChange(e) {
      this.file = e.target.files[0];
    },
    async uploadFile() {
      const chunkSize = 1 * 1024 * 1024; // 1MB
      const chunks = Math.ceil(this.file.size / chunkSize);
      const promises = [];

      for (let i = 0; i < chunks; i++) {
        const start = i * chunkSize;
        const end = ((i + 1) * chunkSize) >= this.file.size ? this.file.size : (i + 1) * chunkSize;
        const chunk = this.file.slice(start, end);

        const formData = new FormData();
        formData.append('file', chunk);
        formData.append('chunkNumber', i);
        formData.append('totalChunks', chunks);

        const promise = axios.post('/upload', formData, {
          onUploadProgress: (progressEvent) => {
            this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          }
        });

        promises.push(promise);
      }

      await Promise.all(promises);
      await axios.post('/merge', { identifier: this.file.name, totalChunks: chunks });
      this.progress = 100;
    },
  },
};
</script>

通过以上技术的综合应用,可以显著提升文件上传的效率和稳定性,为用户提供更好的使用体验。这些技术不仅适用于大文件上传,也可以优化普通文件的上传速度和可靠性。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号