關於大文件上傳

圖片描述

關於大文件上傳

思路

  • 使用js讀取form表單中選擇的file,計算文件md5值,並上傳md5值到服務端,檢查文件是否已上傳過(相似秒傳功能)
  • 若文件未上傳過,按照其大小切成1MB大小的塊,小於1MB的不用切
  • 用ajax異步提交切好的塊上傳至服務端(一個塊一個請求,不阻塞,多線程)
  • 當上傳完成全部切塊,發起一個合併文件的請求,服務端進行前面上傳的文件塊的合併,合併完成即上傳完成。

實現

js計算文件md5使用spark-md5.js,聽說這個庫使用的是世界上最快的md5算法。javascript

js對文件切片並使用ajax上傳切片:php

let size = file.size; //獲取文件大小
const shardSize = 1024 * 1024; // 塊大小1MB
let shardCount = Math.ceil(size/shardSize); //可切成的塊數

for(let i = 0; i < shardCount; i++){
  let start = i * shardSize,
      end = Math.min(size, start + shardSize);
  let form = new FormData();
  form.append('file', file.slice(start, end));  //用slice方法切片
  form.append('size', end - start);
  form.append('name', name);
  form.append('total', shardCount);
  form.append('md5', file_md5); //文件md5值
  form.append('index', i);  //第幾塊

  $.ajax({
    url: 'upload.php?type=shard',
    type: "POST",
    data: form,
    // async: false,     //是否異步上傳,默認true
    processData: false, //很重要,告訴jquery不要對form進行處理
    contentType: false, //很重要,指定爲false才能造成正確的Content-Type
    success: function (res) {
      // 成功回調
    }
  }
}

php端保存切片html

$path = __DIR__ . '/uploads';
$file = $_FILES['file'];
$total = $_POST['total'];
$index = $_POST['index'];
$size = $_POST['size'];
$dst_file = $path . '/' . $name . '-' . $total . ':' . $index;  // 切片文件存儲的文件名 
if ($file["error"] > 0) {
    echo json_encode(['code'=>400, 'msg'=>$file["error"]]);die;
} else {
    $res = move_uploaded_file($file['tmp_name'], $dst_file);
    if ($res) {
        file_put_contents($dst_file . '.info', $size);  // 切片上傳成功,寫一個保存其大小的文件,後續合併是校驗文件用的到
        echo json_encode(['code'=>200, 'msg'=>'shard ok']);die;
    } else {
        echo json_encode(['code'=>400, 'msg'=>'shard move_uploaded_file error']);die;
    }
}

php端合併java

function mergeFile($name, $total, &$msg)
{
    // 校驗切片文件是否都上傳完成,是否完整
    for ($i = 0; $i < $total; $i++) { 
        if (!file_exists($name . '-' . $total . ':' . $i . '.info') || !file_exists($name . '-' . $total . ':' . $i)) {
            $msg = "shard error $i";
            return false;
        } else if (filesize($name . '-' . $total . ':' . $i) != file_get_contents($name . '-' . $total . ':' . $i . '.info')) {
            $msg = "shard size error $i";
            return false;
        }
    }
    @unlink($name);
    if (file_exists($name . '.lock')) {   //加鎖 防止有其餘進程寫文件,形成文件損壞
        $msg = 'on lock';
        return false;
    }
    touch($name . '.lock');
    $file = fopen($name, 'a+');
    for ($i = 0; $i < $total; $i++) {   //按切片順序寫入文件
        $shardFile = fopen($name . '-' . $total . ':' . $i, 'r');
        $shardData = fread($shardFile, filesize($name . '-' . $total . ':' . $i));
        fwrite($file, $shardData);
        fclose($shardFile);
        unlink($name . '-' . $total . ':' . $i); 
        unlink($name . '-' . $total . ':' . $i . '.info');
    }
    fclose($file);
    unlink($name . '.lock');
    return true;
}

我也寫好了一個demo,傳送門jquery

下面是這個demo的效果圖:ajax

v2-4ae5c4707c04f951d0bfc63000ec87e6_hd.jpg

v2-715c32a20abfc85c2e0499e669d106ff_hd.jpg

這個demo有些方面還不夠完善,後續持續完善吧~算法

原文鏈接:json

關於大文件上傳微信


更多分享知識點,請掃碼關注微信公衆號:多線程

圖片描述

相關文章
相關標籤/搜索