大文件切片功能

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

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;
}

原文地址:https://www.cnblogs.com/mxyr/p/10343039.html

时间: 2024-11-02 11:35:58

大文件切片功能的相关文章

Flash大文件断点续传功能

一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content-Range实体头.HTTP协议本身不支持断点上传,需要自己实现. 二.Range 用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式: Range:用于客户端到服务端的请求,可以通过改字段指定下载文件的某一段大小及其单位,字节偏移从0开始.典型格式: Ranges:    (unit=

abp大文件附件功能视频教程

视频分享地址:https://share.weiyun.com/5wtofib 原文地址:https://www.cnblogs.com/jionsoft/p/11847053.html

【原创】用JAVA实现大文件上传及显示进度信息

用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 一. 大文件上传基础描述: 各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容. 比如: Spring 框架中使用类似CommonsMultipartFile对象处理表二进制文件信息. 而.NET 中使用HtmlInputFile/ HttpPostedFile对象处理二进制文件信息. 优点:使用框架内置对象可以很方便的

java实现大文件上传

文件上传是最古老的互联网操作之一,20多年来几乎没有怎么变化,还是操作麻烦.缺乏交互.用户体验差. 一.前端代码 英国程序员Remy Sharp总结了这些新的接口 ,本文在他的基础之上,讨论在前端采用HTML5的API,对文件上传进行渐进式增强: * iframe上传 * ajax上传 * 进度条 * 文件预览 * 拖放上传 1.1 传统形式 文件上传的传统形式,是使用表单元素file,参考 http://www.ruanyifeng.com/blog/2012/08/file_upload.h

java+web+大文件上传下载

文件上传是最古老的互联网操作之一,20多年来几乎没有怎么变化,还是操作麻烦.缺乏交互.用户体验差. 一.前端代码 英国程序员Remy Sharp总结了这些新的接口 ,本文在他的基础之上,讨论在前端采用HTML5的API,对文件上传进行渐进式增强:     * iframe上传  * ajax上传  * 进度条  * 文件预览  * 拖放上传 1.1 传统形式 文件上传的传统形式,是使用表单元素file,参考 http://www.ruanyifeng.com/blog/2012/08/file_

netty4 实现一个断点上传大文件功能

我本来以为文件断点续传功能很简单,不就是提供2个方法: 一个返回已经上传的文件的长度:另外一个负责上传文件呗(请求带上content-range 指明本次上传的内容在整个文件中的位置),然后根据请求提供的位置写呗,太简单了. 但是实际情况还是比较复杂的,关键问题是,上面的描述现在想想只能称作为文件分段上传,而不是断点续传. 断点意味着网络会断,然后断了之后,服务端根本获取不到本次上传的内容,于是下次又只能从头开始传文件.一种解决办法是客户端将文件分成很小的片段(单个片段丢了就整个片段重传),这个

php 读取功能分割大文件实例详解

在php中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file.file_get_contents之类的函数.但当所操作的文件是一个比较大的文件时,这些函数可能就显的力不从心, 下面将从一个需求入手来说明对于读取大文件时,常用的操作方法. 需求如下: 现有一个1G左右的日志文件,大约有500多万行, 用php返回最后几行的内容.实现方法:1. 直接采用file函数来操作注:由于 file函数是一次性将所有内容读入内存,而php为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,

Electron中实现大文件上传和断点续传功能

Electron官网的描述:Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的. 从官网的描述我们可以简单的概括,Electron是开源的框架,可以使用h5来开发跨平台pc桌面应用,这样前端开发这可以开发桌面应用了.由于它是基于Chromium和Node.js开发的,所以在Ele

iOS大文件分片上传和断点续传

总结一下大文件分片上传和断点续传的问题.因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况.http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块),但这不是我们现在说的重点,我们要做的事是保证在网络中断后1G的文件已上传的那部分在下次网络连接时不必再重传.所以我们本地在上传的时候,要将大文件进行分片,比如分成1024*1024B,即将大文件分成1M的片进行上传,服务器在接收后,再将这些片合并成原始文件,这就是分片的基本原理.断点续