php分片上传

前端代码

<!--文件断点、分片上传-->
<template>
  <div class="component-FileSliceUpload">
    <el-upload
        v-if="showBtn"
        :show-file-list="false"
        :limit="1"
        action
        class="mirror-upload"
        :http-request="checkUploadProgress"
        ref="upload">
      <el-button type="primary">点击上传</el-button>
    </el-upload>
    <div v-else style="margin: 20px auto;">
      上传中:<el-progress :percentage="progress" :indeterminate="true" />
    </div>
  </div>
</template>

<script>
import md5 from './md5.min'
import axios from "axios";
export default {
  name: "FileSliceUpload",
  components: {},
  props: {
    //分片的大小,默认50MB
    sliceSize:{
      type:Number,
      default:1024 * 1024 * 50
    },
    //文件大小限制,默认2GB
    sizeLimit:{
      type:Number,
      default: 1024 * 1024 * 1024 * 2
    },
    //接口地址
    actionUrl:{
      type:String,
      default:'/fz/module/upload'
    }
  },
  data() {
    return {
      showBtn:true,
      progress:0,
      controller:null,
      fileCodes:[]
    }
  },
  created() {
  },
  mounted() {
    const storageCodes = window.localStorage.getItem('FZ_UPLOAD_FILE_CODE');
    if(storageCodes) this.fileCodes = JSON.parse(storageCodes);
  },
  filters: {},
  watch: {},
  computed: {},
  methods: {
    checkUploadProgress(res){
      if(res.file.size > this.sizeLimit){
        this.showBtn = true;
        this.$popTip.warning('文件大小超出2G,取消上传!')
        return
      }

      const reader = new FileReader();
      const fileUniStr = [res.file.name,res.file.size,res.file.type,res.file.lastModified].toString()
      this.$emit('processFile',true)
      reader.addEventListener('load',e=>{
        const fileCode = md5(fileUniStr);
        const storageCode = this.fileCodes?.find(i=>i.code===fileCode);
        if(storageCode){
          this.uploadInit(res,storageCode['lastSlice']+1,fileCode);
        }else{
          this.setUploadProgress(fileCode,0);
          this.uploadInit(res,1,fileCode);
        }
      });
      reader.readAsArrayBuffer(res.file);
    },
    setUploadProgress(fileCode,sliceIndex){
      if(sliceIndex === 0){
        this.fileCodes.push({code:fileCode,lastSlice:sliceIndex})
      }else if(sliceIndex === -1){
        this.fileCodes.splice(this.fileCodes.findIndex(i=>i.code===fileCode),1)
      }else{
        this.fileCodes.find(i=>i.code===fileCode)['lastSlice'] = sliceIndex
      }
      window.localStorage.setItem('FZ_UPLOAD_FILE_CODE',JSON.stringify(this.fileCodes))
    },
    uploadInit(file,startSlice,fileCode){
      const blob = file.file;
      const fileSize = blob.size;
      const fileFullName = blob.name;
      const totalSlice = Math.ceil(fileSize / this.sliceSize);

      this.showBtn = false;
      this.controller = this.uploadController(totalSlice,blob,fileFullName,fileSize,startSlice,fileCode)
      this.controller.next()
    },
    * uploadController(totalSlice,blob,fileFullName,fileSize,startSlice,fileCode){
      for (let i = startSlice - 1; i < totalSlice; i++) {
        let start = i * this.sliceSize;
        let formData = new FormData()
        formData.append('file',blob.slice(start, Math.min(fileSize, start + this.sliceSize)));
        formData.append('fileName',fileFullName.replace(/(.*)\..*/,'$1'))
        formData.append('fileExt',fileFullName.replace(/.*\.(.*)/,'$1'))
        formData.append('page',(i+1).toString())
        formData.append('totalPage',totalSlice.toString())
        const reset = yield this.handleSliceUpload(formData,i+1,totalSlice,fileCode);
        if(reset) {
          i = totalSlice
          this.progress = 0
          this.showBtn = true
        }
      }
    },
    handleSliceUpload(formData,sliceIndex,totalSlice,fileCode){
      return this.sliceUploadApi(formData).then(res=>{
        if(res.data.errorcode === 0){
          this.progress = Math.round(((sliceIndex)/totalSlice).toFixed(2) * 100);
          setTimeout(async ()=>{
            if(res.data.status===2){
              this.progress = 100
              this.showBtn = true
              this.setUploadProgress(fileCode,-1)
              this.$message.success('上传成功!');
              this.$emit('success',res)
              this.$emit('processFile',false)
            }else if(res.data.status===3){
              console.log('分片丢失:',res);
              this.$emit('processFile',false)
            }else{
              this.setUploadProgress(fileCode,sliceIndex)
              this.controller.next()
            }
          },500)
        }else{
          this.$confirm('发生了一些错误,是否重试?').then(()=>{
            this.handleSliceUpload(formData,sliceIndex,totalSlice,fileCode)
          }).catch(()=>{
            this.controller.next(true)
            this.$message.info('已取消上传!');
            this.$emit('processFile',false)
          })
        }
      }).catch(e=>{
        this.$confirm('服务器未响应,请检查网络连接与服务器状态后重试!','请求失败',
            {type:'error',confirmButtonText:'重试',cancelButtonText:'停止上传'}
        ).then(()=>{
          this.handleSliceUpload(formData,sliceIndex,totalSlice,fileCode)
        }).catch(()=>{
          console.log(e);
          this.controller.next(true)
          this.$message.info('已取消上传!');
          this.$emit('processFile',false)
        })
      })
    },
    sliceUploadApi(formData){
      const service = axios.create({
        baseURL: this.getApiUrl(), // api的base_url
        timeout: 1000 * 10 // 请求超时时间
      })
      return service.post(this.actionUrl,formData)
    }
  },
}
</script>

<style lang="scss" scoped>
.component-FileSliceUpload {

}
</style>

php代码

/**
     * 分片上传
     * @param $file
     * @param $piece_info
     * @return array
     * @throws \Exception
     */
    public static function piecewiseUpload($file, $piece_info): array
    {
        if (empty($file)) {
            throw new \Exception("请先上传文件");
        }

        $fileName = $piece_info['fileName'];
        $fileExt = $piece_info['fileExt'];
        $page = $piece_info['page'];
        $totalPage = $piece_info['totalPage'];
        $fileTmpName = $file['file']['tmp_name'];
        $piece_info['md5'] = md5($fileName . "." . $fileExt);

        if ($fileName == '' || $fileExt == '' || $page == '' || $totalPage == '' || $fileTmpName == '') {
            return ['status' => 500];
        }

        $status = 1;
        $downUrl = '';

        $uploadPath = getConst::getConst('FILE_URL') . '/tmp/piece/';
        if (!is_dir($uploadPath)) {
            mkdir($uploadPath);
        }
        $piece_path = getConst::getConst('FILE_URL') . '/tmp/piece/' . md5($fileName) . '/';
        if (!is_dir($piece_path)) {
            mkdir($piece_path);
        }
        $upload_path = getConst::getConst('FILE_URL') . '/tmp/upload/';
        if (!is_dir($upload_path)) {
            mkdir($upload_path);
        }
        // 上传文件要保存的路径
        $fname = sprintf("$piece_path%s-%s.%s", $fileName, $page, $fileExt);
        \SysTools::SysLog("分片已上传, 分片地址:" . $fname . " 当前分片数是:$page,总的分片数是:$totalPage");
        $data = file_get_contents($fileTmpName);
        @file_put_contents($fname, $data);

        $service = new FileService();
        $missing_piece = [];
        // 最后一片文件
        if ($totalPage == $page) {
            $status = 2;

            $uploadFileName = sprintf("$upload_path%s.%s", $fileName, $fileExt);

            // 合并文件,删除分片文件
            for ($i = 1; $i <= $totalPage; $i++) {
                $tmp = sprintf("$piece_path%s-%s.%s", $fileName, $i, $fileExt);
                if (!is_file($tmp)) {
                    $missing_piece[] = $i;
                    \SysTools::SysLog("分片缺失, 缺失的分片是第{$i}片,分片地址");
                }
            }

            if (count($missing_piece) == 0) {
                for ($i = 1; $i <= $totalPage; $i++) {
                    $tmp = sprintf("$piece_path%s-%s.%s", $fileName, $i, $fileExt);
                    $data = file_get_contents($tmp);
                    @file_put_contents($uploadFileName, $data, FILE_APPEND);
                    @unlink($tmp);
                }
                $service->rmrf($piece_path);
            } else {
                $status = 3;
            }

            $downUrl = "http://" . getConst::getConst('FILE_DOMAIN') . $uploadFileName;
        }

        // 返回上传状态
        return ['status' => $status, 'downUrl' => $downUrl, 'missing_piece' => $missing_piece];
    }



 /**
     * @return array
     * @throws \RedisException
     * @throws \Exception
     */
    public static function checkPieceIntact(): array
    {
        $redis = \CacheTools::get_redis(14);
        $piece_key = 'fz_piece_upload_single_file';
        if (!$redis->get($piece_key)) {
            throw new \Exception("当前分片丢失,请重新上传");
        }
        $piece_info = json_decode($redis->get($piece_key), true);

        $fileName = $piece_info['fileName'];
        $fileExt = $piece_info['fileExt'];
        $page = $piece_info['page'];
        $totalPage = $piece_info['totalPage'];

        $upload_path = getConst::getConst('FILE_URL') . '/tmp/upload/';
        if (!is_dir($upload_path)) {
            mkdir($upload_path);
        }
        $uploadFileName = sprintf("$upload_path%s.%s", $fileName, $fileExt);
        if (is_file($uploadFileName)) {
            return [];
        }

        $piece_path = getConst::getConst('FILE_URL') . '/tmp/piece/' . md5($fileName) . '/';
        if (!is_dir($piece_path)) {
            mkdir($piece_path);
        }
        $intact = [];
        for ($i = 1; $i <= $totalPage; $i++) {
            $tmp = sprintf("$piece_path%s-%s.%s", $fileName, $i, $fileExt);
            if (!is_file($tmp)) {
                $intact[$i] = 0;
                \SysTools::SysLog("分片缺失, 缺失的分片是第{$i}片,分片地址");
            } else {
                $intact[$i] = 1;
            }
        }

        return $intact;
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值