大文件切片功能
近期接到的新需求:上传大文件,因文件上传的大小不定,所以需要切片上传
前端代码:
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; }