大文件切片功能

Posted mxyr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大文件切片功能相关的知识,希望对你有一定的参考价值。

近期接到的新需求:上传大文件,因文件上传的大小不定,所以需要切片上传
前端代码:

import md5 from "js-md5";

/**
 * 缓存转换  导入大文件
 * @param {*} that   this
 * @param {*} file 上传文件的对象
 * @param {*} size   每次上传文件的限制大小
 * @param {*} progress 上传文件的进度条
 * @param {*} url string 上传文件的url

 * @param {*} module_name  string   上传文件的需要上文的文件名
 */
export function uploadFile(that, file, size, progress, url, module_name) {
  let xhr = new XMLHttpRequest();
  let form_data = new FormData();
  let start = 0;
  let end = start + that.size;
  let blob;
  let blob_num = 1;

  blob = cutFile(file);
  sendFile(blob, file);
  blob_num += 1;

  //切割文件
  function cutFile(file) {
    let file_blob = file.slice(start, end);
    start = end;
    end = start + that.size;
    return file_blob;
  };

  //发送文件
  function sendFile(blob, file) {
    let total_blob_num = Math.ceil(file.size / size);
    form_data = new FormData();
    form_data.append(‘file‘, blob);
    form_data.append(‘blob_num‘, blob_num);
    form_data.append(‘total_blob_num‘, total_blob_num);
    form_data.append(‘file_name‘, file.name);
    form_data.append(‘module_name‘, module_name);
    form_data.append(‘code‘, md5(md5(file.name + global.ENCRYPTION_KEY)));
    xhr.open("POST", global.IMG_URL + url, false);
    xhr.onreadystatechange = function () {
      if (total_blob_num == 1) {
        that.progress = 100;
      } else {
        that.progress = Math.min(100, (blob_num / total_blob_num) * 100);
      }
      if (JSON.parse(xhr.response).code == 0) {
        that.file_name = ‘‘;
        that.$Message.error(‘上传错误‘);
        return false
      } else if (JSON.parse(xhr.response).code == 2) {
        that.$Message.success(‘上传成功‘);
        that.file_path = JSON.parse(xhr.response).file_path;
        return false
      }
      let t = setTimeout(function () {
        if (start < file.size) {
          blob = cutFile(file);
          sendFile(blob, file);
          blob_num += 1;
        } else {
          clearTimeout(t);
        }
      }, 1000);
    }
    xhr.send(form_data);
  }
}

后端代码:我们后端是用php完成的

<?php

if (!defined(‘BASEPATH‘)) exit(‘No direct script access allowed‘);
header(‘Content-Type:text/html;charset=utf-8‘);

header(‘Access-Control-Allow-Origin:*‘);

class chunkupload extends CI_Controller
{
    private $filepath; // 上传目录
    private $tmpPath; // PHP文件上传临时目录
    private $blobNum; // 当前第几个文件块
    private $totalBlobNum; // 文件块总数
    private $fileName; // 原文件名
    private $finalFileName; // 经过处理的最终文件名

    public function __construct()
    {
        parent::__construct();
    }

    // 大文件分片上传
    public function bigFileUpload()
    {
        if (empty($_POST[‘code‘])) {
            echo_json(0,‘code不能为空‘);
        }

        if (empty($_POST[‘module_name‘])) {
            echo_json(0,‘上传目录不能为空‘);
        }

        if (empty($_POST[‘blob_num‘])) {
            echo_json(0,‘当前片数不能为空‘);
        }

        if (empty($_POST[‘file_name‘])) {
            echo_json(0,‘文件名不能为空‘);
        }

        if (empty($_POST[‘total_blob_num‘])) {
            echo_json(0,‘总片数不能为空‘);
        }

        if (empty($_FILES[‘file‘])) {
            echo_json(‘file为空‘);
        }

        if (empty($_FILES[‘file‘][‘tmp_name‘])) {
            echo_json(0,‘tmp_name为空‘);
        }

        $fileDir = ‘./uploads/‘.trim($_POST[‘module_name‘],‘/‘).‘/‘ . date(‘Y/m/d‘);
        $this->filepath = $fileDir;
        $this->tmpPath = $_FILES[‘file‘][‘tmp_name‘];
        $this->blobNum = $_POST[‘blob_num‘];
        $this->totalBlobNum = $_POST[‘total_blob_num‘];
        $this->fileName = $_POST[‘file_name‘];

        // 校验
        $this->validate($_POST[‘code‘], $_POST[‘file_name‘]);
        // 移动文件
        $this->moveFile();
        // 合并分块的文件
        $this->fileMerge();
        // 响应状态
        $this->apiReturn();
    }

    // 判断是否是最后一块,如果是则进行文件合成并且删除文件块
    private function fileMerge()
    {
        if($this->blobNum == $this->totalBlobNum){
            $blob = ‘‘;
            for($i=1; $i<= $this->totalBlobNum; $i++){
                $blob .= file_get_contents($this->filepath.‘/‘. $this->fileName.‘__‘.$i);
            }

            $ext = ‘.‘.substr(strrchr($this->fileName, ‘.‘), 1);
            $this->finalFileName = date(‘YmdHis‘) . rand(‘10000‘, ‘99999‘) . $ext;

            file_put_contents($this->filepath.‘/‘. $this->finalFileName,$blob);
            $this->deleteFileBlob();
        }
    }

    // 删除文件块
    private function deleteFileBlob()
    {
        for($i=1; $i<= $this->totalBlobNum; $i++){
            @unlink($this->filepath.‘/‘. $this->fileName.‘__‘.$i);
        }
    }

    // 移动文件
    private function moveFile()
    {
        $this->touchDir();
        $filename = $this->filepath.‘/‘. $this->fileName.‘__‘.$this->blobNum;
        move_uploaded_file($this->tmpPath,$filename);
    }

    // API返回数据
    public function apiReturn()
    {
        header(‘Content-type: application/json‘);
        if($this->blobNum == $this->totalBlobNum){
            if(file_exists($this->filepath.‘/‘. $this->finalFileName)){
                $data[‘code‘] = 2;
                $data[‘msg‘] = ‘success‘;
                $data[‘file_path‘] = ltrim($this->filepath,‘./‘).‘/‘. $this->finalFileName;
                echo json_encode($data);
                exit;
            }
        }else{
            if(file_exists($this->filepath.‘/‘. $this->fileName.‘__‘.$this->blobNum)){
                $data[‘code‘] = 1;
                $data[‘msg‘] = ‘上传中,共:‘.$this->totalBlobNum.‘块,当前第‘.$this->blobNum.‘块....‘;
                $data[‘file_path‘] = ‘‘;
                echo json_encode($data);
                exit;
            }
        }

    }

    // 建立上传文件夹
    private function touchDir()
    {
        if(!file_exists($this->filepath)){
            return mkdir($this->filepath, 0777, true);
        }
    }

    // 参数校验
    private function validate($code,$fileName)
    {
        if (md5(md5($fileName.config_item(‘encryption_key‘))) != $code) {
            echo_json(0,‘参数校验失败‘);
        }
    }
}

function echo_json($code = 0, $msg = ‘‘, $data = array())
{
    $arr = array(
        ‘code‘ => $code,
        ‘msg‘ => $msg,
        ‘data‘ => $data
    );

    echo json_encode($arr);
    exit;
}

 


以上是关于大文件切片功能的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地打开 30gb 的文件并处理其中的片段而不减慢速度?

c#大文件上传详解及实例代码

ffmpeg 获取音频文件PCM切片

vue2.0 代码功能片段

VSCode自定义代码片段13——Vue的状态大管家

VSCode自定义代码片段13——Vue的状态大管家