+
80
-

浏览器上js如何实现上传大文件页面刷新后断点续传?

浏览器上js如何实现上传大文件页面刷新后断点续传?


网友回复

+
0
-

思路很简单,就是将大文件分块切割列出一个编号和清单,然后设置并发数同时上传发给后端,后端根据分块和编号清单数据最后组装还原成原始的大文件,上传的过程中将已完成的批次保存到localstorage中,下次刷新后直接发送未完成部分。

示例代码:

前端 JavaScript 代码:

document.getElementById('fileUpload').addEventListener('change', handleFileSelect, false);

function handleFileSelect(event) {
  const file = event.target.files[0];
  const chunkSize = 1024 * 1024; // 比如我们设定每个分块的大小为1MB。
  let chunks = Math.ceil(file.size / chunkSize);
  let currentChunk = 0;
  const concurrency = 3; // 并发上传的数目。
  let offset = 0; // 用于记录当前并发上传块的位置。

  // 读取localStorage中的上传进度。
  function getUploadProgress() {
    let progress = localStorage.getItem(file.name);
    return progress ? parseInt(progress, 10) : 0;
  }

  // 保存上传进度到localStorage。
  function saveUploadProgress(chunk) {
    localStorage.setItem(file.name, chunk.toString());
  }

  // 创建分块上传的函数。
  function uploadChunk(chunk) {
    var start = chunk * chunkSize;
    var end = Math.min(file.size, start + chunkSize);
    var formData = new FormData();
    formData.append('file', file.slice(start, end)); // 获取文件的某个片段。
    formData.append('fileName', file.name);
    formData.append('fileSize', file.size);
    formData.append('chunk', chunk); // 当前片段的编号。
    formData.append('chunks', chunks); // 总片段数。

    fetch('upload.php', {
      method: 'POST',
      body: formData
    }).then(response => response.text()).then(data => {
      if (data === 'success') {
        // 如果块上传成功,则保存进度,并尝试上传下一个块。
        saveUploadProgress(chunk + 1);
        if (currentChunk < chunks - 1) {
          currentChunk++;
          uploadNextChunk();
        }
      } else {
        // 如果上传失败,再次尝试上传该块。
        uploadChunk(chunk);
      }
    }).catch(err => console.error('上传块出错:', err));
  }

  // 同时上传下一个块的函数。
  function uploadNextChunk() {
    while (offset < concurrency && currentChunk + offset < chunks) {
      uploadChunk(currentChunk + offset++);
    }
    offset = 0; // 重置offset用于下一个并发上传批次。
  }

  // 断点续传逻辑,根据进度决定从哪个块开始上传。
  currentChunk = getUploadProgress();

  // 开始上传。
  uploadNextChunk();
}

PHP 后端代码 (upload.php):
<?php
$targetDir = "uploads/"; // 上传文件存储目录。
$fileName = basename($_POST['fileName']); // 上传文件的原始名称。
$fileSize = $_POST['fileSize']; // 上传文件的原始大小。
$chunk = $_POST['chunk']; // 当前片段的编号。
$chunks = $_POST['chunks']; // 总片段数。

// 以文件名和分块编号生成临时文件名,避免不同会话中的文件冲突。
$tempFilePath = $targetDir . $fileName . '.' . $chunk;

// 把文件分块先保存下来。
if (!move_uploaded_file($_FILES['file']['tmp_name'], $tempFilePath)) {
    echo 'fail';
    return;
}

// 如果是最后一块,则合并所有的片段。
if ($chunk == $chunks - 1) {
    $finalFilePath = $targetDir . $fileName;
    $file = fopen($finalFilePath, 'ab');

    for ($i = 0; $i < $chunks; $i++) {
        $tmpFilePath = $targetDir . $fileName . '.' . $i;
        $chunkFile = fopen($tmpFilePath, 'rb');
        $content = fread($chunkFile, filesize($tmpFilePath));
        fwrite($file, $content);

        fclose($chunkFile);
        unlink($tmpFilePath); // 删除分块文件。
    }
    fclose($file);
}

echo 'success';

在这个例子里,我们在前端将文件分成了多个块,并使用 fetch API 上传这些块到一个名为 upload.php 的PHP脚本。PHP脚本处理上传的分块,将它们保存到服务器的特定目录,并在上传完所有分块之后将它们合并成原始文件。

这只是一个例子,在生产环境中,你需要增加很多安全和性能优化的措施。这包括处理并发和竞态条件、验证文件完整性、增加验证上传进度的API端点以及可能的权限检查和加密措施。此外,也需要测试并优化上传性能,以适应不同网络和服务器环境。

我知道答案,我要回答