欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue+Minio實(shí)現(xiàn)多文件進(jìn)度上傳的詳細(xì)步驟

 更新時(shí)間:2022年03月17日 16:19:24   作者:徐皮皮  
這篇文章主要給大家介紹了關(guān)于如何利用vue+Minio實(shí)現(xiàn)多文件進(jìn)度上傳的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

背景

最近突然接到了一個(gè)產(chǎn)品的需求,有點(diǎn)特別,在這里給大家分享一下,需求如下

  • 提交表單,同時(shí)要上傳模型資源
  • 模型文件是大文件,要顯示上傳進(jìn)度,同時(shí)可以刪除
  • 模型文件要上傳到服務(wù)器,表單數(shù)據(jù)同步到數(shù)據(jù)庫(kù)
  • 同時(shí)要同步上傳后的模型地址到數(shù)據(jù)庫(kù)
  • 后端使用Minio做文件管理

設(shè)計(jì)圖如下

一開(kāi)始以為是一個(gè)簡(jiǎn)單的表單上傳,發(fā)現(xiàn)并不是,這是大文件上傳啊,但很快又發(fā)現(xiàn),不單單是上傳大文件,還有將文件信息關(guān)聯(lián)到表單。

基于這個(gè)奇葩的情況,我和后端兄弟商量了一下,決定使用如下方案

實(shí)現(xiàn)方案

分2步走

  • 點(diǎn)擊上傳時(shí),先提交表單信息到數(shù)據(jù)庫(kù),接著后端返回一個(gè)表單的id給我
  • 當(dāng)所有文件上傳完成后,再調(diào)用另外一個(gè)服務(wù),將上傳完成后的地址和表單id發(fā)送給后端

如此,便完成了上面的需求

了解一下Mino

這里大家先了解一下Minio的js SDK文檔

里面有2個(gè)很重要的接口,今天要用到

一個(gè)是給文件生成用于put方法上傳的地址
一個(gè)是獲取已經(jīng)上傳完成后的文件的get下載地址

實(shí)現(xiàn)步驟

這里是使用原生的 ajax請(qǐng)求進(jìn)行上傳的,至于為什么,后面會(huì)有說(shuō)到

1.創(chuàng)建存儲(chǔ)桶

創(chuàng)建一個(gè)Minio上傳實(shí)例

var Minio = require('minio')

this.minioClient = new Minio.Client({
    endPoint: '192.168.172.162', //后端提供
    port: 9000, //端口號(hào)默認(rèn)9000
    useSSL: true,
    accessKey: 'Q3AM3UQ867SPQQA43P2F', //后端提供
    secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
});

this.userBucket = 'yourBucketName' //這里后端需要提供給你,一個(gè)存儲(chǔ)桶名字

2.選擇文件

這里使用input標(biāo)簽選擇文件,點(diǎn)擊選擇文件的時(shí)候,調(diào)用一下input的click方法,就可以打開(kāi)本地文件夾

<el-form-item label="資源文件">
  <el-button
    style="marginRight:10px;"
    @click="selectFile()"
    size="mini"
  >選擇文件</el-button>
  <input
    :accept="acceptFileType"
    multiple="multiple"
    type="file"
    id="uploadInput"
    ref="uploadInput"
    v-show="false"
    @change="getAndFormatFile()"
  >
  <i class="tip">僅支持.gbl、.gltf、.fbx、.obj、.mtl、.hdr、.png、.jpg格式的文件</i>
  <i class="tip">單個(gè)文件的大小限制為128MB</i>
</el-form-item>
selectFile() {
    let inputDOM = this.$refs.uploadInput
    inputDOM.click();
},

接著就是對(duì)文件進(jìn)行格式化

//格式化文件并創(chuàng)建上傳隊(duì)列
  getAndFormatFile(){
    let files = this.$refs.uploadInput.files
    const userBucket = this.userBucket
    if(files.length > 6) {
      this.$message({
        message: `最大只能上傳6個(gè)文件`,
        type: 'warning'
      })
      return
    }
    files.forEach((file, index) => {
      if ((file.size / 1024 / 1024).toFixed(2) > 128) { //單個(gè)文件限制大小為128MB
        this.$message({
          message: `文件大小不能超過(guò)128MB`,
          type: 'warning'
        })
        return
      }
      //創(chuàng)建文件的put方法的url
      this.minioClient.presignedPutObject(userBucket, file.name, 24 * 60 * 60, (err, presignedUrl) => {
        if (err) {
          this.$message({
            message: `服務(wù)器連接超時(shí)`,
            type: 'error'
          })
          return err
        }
        let fileIcon = this.getFileIcon(file)
        let fileUploadProgress = '0%' //文件上傳進(jìn)度
        this.fileInfoList.push({
          file, //文件
          fileIcon, //文件對(duì)應(yīng)的圖標(biāo) className
          fileUploadProgress, //文件上傳進(jìn)度
          filePutUrl: presignedUrl, //文件上傳put方法的url
          fileGetUrl: '', //文件下載的url
        })
      })
    })
    this.fileList = [...this.fileInfoList]
  },

3.創(chuàng)建上傳隊(duì)列

這里定義了一個(gè)創(chuàng)建文件上傳請(qǐng)求的方法,使用原生的XMLHttpRequest,它接受以下參數(shù)

  • file:要上傳的文件
  • filePutUrl:文件上傳的put方法地址
  • customHeader: 自定義的頭信息
  • onUploadProgress:文件上傳的進(jìn)度監(jiān)聽(tīng)函數(shù)
  • onUploaded:文件上傳完成的監(jiān)聽(tīng)函數(shù)
  • onError:文件上傳出錯(cuò)的監(jiān)聽(tīng)函數(shù)
 //創(chuàng)建上傳文件的http
  createUploadHttp(config){
    const {file, filePutUrl, customHeader, onUploadProgress, onUploaded, onError} = config
    let fileName = file.name
    let http = new XMLHttpRequest();
    http.upload.addEventListener("progress", (e) => { //監(jiān)聽(tīng)http的進(jìn)度。并執(zhí)行進(jìn)度監(jiān)聽(tīng)函數(shù)
      onUploadProgress({
        progressEvent: e,
        uploadingFile: file
      })
    }, false)

    http.onload = () => {
      if (http.status === 200 && http.status < 300 || http.status === 304) {
        try {
        //監(jiān)聽(tīng)http的完成事件,并執(zhí)行上傳完成的監(jiān)聽(tīng)函數(shù)
          const result = http.responseURL
          onUploaded({ result, uploadedFile: file})
        } catch(error) {
        //監(jiān)聽(tīng)錯(cuò)誤
          onError({ error, errorFile: file})
        }
      }
    }
 
    http.open("PUT", filePutUrl, true);
    //加入頭信息
    Object.keys(customHeader).forEach((key, index) =>{
      http.setRequestHeader(key, customHeader[key])
    })
    http.send(file);
    return http //返回該http實(shí)例
  }

4.開(kāi)始上傳

//上傳文件到存儲(chǔ)桶
  async handleUplaod(){
    let _this = this
    if(this.fileInfoList.length < 1) {
      this.$message({
        message: `請(qǐng)先選擇文件`,
        type: 'warning'
      })
      return
    }
    //先上傳文件的基本表單信息,獲取表單信息的id
    try{
      const {remark, alias} = _this.uploadFormData
      let res = await uploadModelSourceInfo({remark, serviceName: alias})
      _this.modelSourceInfoId = res.message
    }catch(error){
      if(error) {
        _this.$message({
          message: `上傳失敗,請(qǐng)檢查服務(wù)`,
          type: 'error'
        })
        return
      }
    }

    //開(kāi)始將模型資源上傳到遠(yuǎn)程的存儲(chǔ)桶
    this.fileList.forEach((item, index) => {
      const {file, filePutUrl} = item
      let config = {
        file,
        filePutUrl,
        customHeader:{
          "X-FILENAME": encodeURIComponent(file.name),
          "X-Access-Token": getToken()
        },
        onUploadProgress: ({progressEvent, uploadingFile}) => {
          let progress = (progressEvent.loaded / progressEvent.total).toFixed(2)
          this.updateFileUploadProgress(uploadingFile, progress)
        },
        onUploaded: ({result, uploadedFile}) => {
          this.updateFileDownloadUrl(uploadedFile)
        },
        onError: ({error, errorFile}) => {

        }
      }

      let httpInstance = this.createUploadHttp(config) //創(chuàng)建http請(qǐng)求實(shí)例
      this.httpQueue.push(httpInstance) //將http請(qǐng)求保存到隊(duì)列中
    })
  },


//更新對(duì)應(yīng)文件的上傳進(jìn)度
updateFileUploadProgress(uploadingFile, progress) {
this.fileInfoList.forEach((item, index) => {
  if(item.file.name === uploadingFile.name){
    item.fileUploadProgress = (Number(progress)*100).toFixed(2) + '%'
  }
})
},

//更新上傳完成文件的下載地址
updateFileDownloadUrl(uploadedFile){
const userBucket = this.userBucket
this.fileInfoList.forEach((item, index) => {
  if(item.file.name === uploadedFile.name){
    this.minioClient.presignedGetObject(userBucket, uploadedFile.name, 24*60*60, (err, presignedUrl) => {
      if (err) return console.log(err)
      item.fileGetUrl = presignedUrl
    })
  }
})
},

5 上傳完成后,同步文件地址給后端

在watch里監(jiān)聽(tīng)文件列表,當(dāng)所有的文件進(jìn)度都是100%時(shí),表示上傳完成,接著就可以同步文件信息

watch:{
  fileInfoList: {
    handler(val){
      //1.3所有文件都上傳到存儲(chǔ)桶后,將上傳完成后的文件地址、文件名字同步后端
      if(val.length < 1) return
      let allFileHasUpload = val.every((item, index) => {
        return item.fileGetUrl.length > 1
      })
     if(allFileHasUpload) {
       this.allFileHasUpload = allFileHasUpload
       const {modelSourceInfoId} = this
       if(modelSourceInfoId.length < 1) {
         return
       }
       const url = process.env.VUE_APP_BASE_API + "/vector-map/threeDimensionalModelService/invokeMapService"
       const files = val.map((ite, idx) => {
         return {
            fileName: ite.file.name,
            fileUrl: ite.fileGetUrl
         }
       })
       this.syncAllUploadedFile(url, files, modelSourceInfoId)
     }

    },
    deep: true
  }
},



//同步已上傳的文件到后端
syncAllUploadedFile(url, files, modelSourceInfoId){
    let xhr = new XMLHttpRequest()
    xhr.onload = () => {
      if (xhr.status === 200 && xhr.status < 300 || xhr.status === 304) {
        try {
         const res = JSON.parse(xhr.responseText)
         if(res && res.code === 200){
           this.$message({
             message: '上傳完成',
             type: 'success'
           })
             this.$emit('close')
             this.fileInfoList = []
             this.fileList = []
             this.httpQueue = []
         }
        } catch(error) {
         this.$message({
           message: '上傳失敗,請(qǐng)檢查服務(wù)',
           type: 'error'
         })
        }
      }
    }
    xhr.open("post", url, true)
    xhr.setRequestHeader('Content-Type', 'application/json')
    xhr.setRequestHeader('X-Access-Token', getToken())
    //將前面1.1獲取文件信息的id作為頭信息傳遞到后端
    xhr.setRequestHeader('ThreeDimensionalModel-ServiceID', modelSourceInfoId)
    xhr.send(JSON.stringify(files))
},

6.刪除文件

刪除文件時(shí)要注意

  • 刪除本地的文件緩存
  • 刪除存儲(chǔ)桶里面的文件
  • 停止當(dāng)前文件對(duì)應(yīng)的http請(qǐng)求
//刪除文件,并取消正在文件的上傳
  deleteFile(fileInfo, index){
    this.httpQueue[index] && this.httpQueue[index].abort()
    this.httpQueue[index] && this.httpQueue.splice(index, 1)
    this.fileInfoList.splice(index, 1)
    this.fileList.splice(index, 1)
    this.removeRemoteFile(fileInfo)
  },

  //清空文件并取消上傳隊(duì)列
  clearFile() {
    this.fileInfoList.forEach((item, index) => {
      this.httpQueue[index] && this.httpQueue[index].abort()
      this.httpQueue[index] && this.httpQueue.splice(index, 1)
      this.removeRemoteFile(item)
    })
    this.fileInfoList = []
    this.httpQueue = []
    this.fileList = []
  },
  //刪除遠(yuǎn)程文件
  removeRemoteFile(fileInfo){
    const userBucket = this.userBucket
    const { fileUploadProgress, file} = fileInfo
    const fileName = file.name
    const complete = fileUploadProgress === '100.00%' ? true : false
    if(complete){
     this. minioClient.removeObject(userBucket, fileName, function(err) {
        if (err) {
          return console.log('Unable to remove object', err)
        }
        console.log('Removed the object')
      })
    }else{
      this.minioClient.removeIncompleteUpload(userBucket, fileName, function(err) {
        if (err) {
          return console.log('Unable to remove incomplete object', err)
        }
        console.log('Incomplete object removed successfully.')
      })
    }
  },

完整代碼

這里的完整代碼是我直接從工程里拷貝出來(lái)的,里面用到了一些自己封裝的服務(wù)和方法 比如 后端的接口、AES解密、獲取Token、表單驗(yàn)證等

import{uploadModelSourceInfo, uploadModelSource, getMinioConfig} from '@/api/map' 
import AES from '@/utils/AES.js' 
import { getToken } from '@/utils/auth' 
import * as myValiDate from "@/utils/formValidate";
/**
 * 文件說(shuō)明
 * @Author: zhuds
 * @Description: 模型資源上傳彈窗
    分為3個(gè)步驟
    1.先將文件的基本表單信息上傳給后端,獲取文件信息的ID
    2.然后將文件上傳存儲(chǔ)桶
    3.等所有文件都上傳完成后,再將上傳完成后的文件信息傳遞給后端,注意,此時(shí)的請(qǐng)求頭要戴上第1步獲取的文件信息id
 * @Date: 2/28/2022, 1:13:20 PM
 * @LastEditDate: 2/28/2022, 1:13:20 PM
 * @LastEditor:
 */
<template>
  <div class="upload-model">
    <el-dialog
    	:visible.sync="isVisible"
    	@close="close()"
    	:show-close ="false"
    	:close-on-click-modal="false"
    	top="10vh"
      v-if="isVisible"
      :destroy-on-close="true"
    >
    	<div slot="title" class="header-title">
    		<div class="icon"></div>           
    		<span>上傳模型資源</span>
    		<i class="el-icon-close"  @click="close()"></i>			       
    	</div>

      <el-form
        :label-position="labelPosition"
        label-width="80px"
        :model="uploadFormData"
        ref="form"
        :rules="rules"
      >
        <el-form-item label="別名">
          <el-input   size="small" v-model="uploadFormData.alias"></el-input>
        </el-form-item>

        <el-form-item label="備注">
          <el-input type="textarea" v-model="uploadFormData.remark"  size="small"></el-input>
        </el-form-item>

        <el-form-item label="資源文件">
          <el-button
            style="marginRight:10px;"
            @click="selectFile()"
            size="mini"
          >選擇文件</el-button>
          <input
            :accept="acceptFileType"
            multiple="multiple"
            type="file"
            id="uploadInput"
            ref="uploadInput"
            v-show="false"
            @change="getAndFormatFile()"
          >
          <i class="tip">僅支持.gbl、.gltf、.fbx、.obj、.mtl、.hdr、.png、.jpg格式的文件</i>
          <i class="tip">單個(gè)文件的大小限制為128MB</i>
        </el-form-item>

      </el-form>

      <div class="file-list" v-show="fileInfoList.length > 0">
        <div class="file-item" v-for="(item, index) in fileInfoList" :key="index">
          <div class="icon"></div>
          <div class="name">{{item.file.name}}</div>
          <div class="size">{{(item.file.size/1024/1024).toFixed(2)}}MB  </div>
          <div class="progress">
            <div class="bar" :style="{width: item.fileUploadProgress}"></div>
          </div>
          <div class="rate">{{item.fileUploadProgress}}</div>


          <div class="delete-btn" @click="deleteFile(item, index)">x</div>
        </div>
      </div>
      <div class="custom-footer">
        <button class="info" @click="close()">取 消</button>
        <button class="success" @click="handleUplaod()">上傳</button>
      </div>

    </el-dialog>
  </div>
</template>

<script>

  import{uploadModelSourceInfo, uploadModelSource, getMinioConfig} from '@/api/map'
  import AES from '@/utils/AES.js'
  import { getToken } from '@/utils/auth'
  import * as myValiDate from "@/utils/formValidate";
  let Minio = require('minio')

  export default {
    name: 'UploadModelDialog',
    props: {
      isVisible: {
        type: Boolean,
        default: false
      },
    },
    data(){
      return {
        labelPosition: 'right',
        uploadFormData: {
          alias: '', //服務(wù)名稱(chēng)
          remark: '', //備注
        },
        rules: {
          serviceName: [{
          	validator: myValiDate.validateServiceName,
          	trigger: "blur",
          	required: true,
          }],
        },
        acceptFileType:".glb,.gltf,.fbx,.obj,.mtl,.hdr,.png,.jpg, .mp4",
        fileList:[], //待上傳的文件列表
        fileInfoList: [], //格式化后的文件信息列表
        userBucket: null,
        httpQueue: [], //上傳文件的http隊(duì)列
        allFileHasUpload: false, //是否完成上傳
        modelSourceInfoId: '', //模型資源基本信息的id
      }
    },

    watch:{
      fileInfoList: {
        handler(val){
          //1.3所有文件都上傳到存儲(chǔ)桶后,將上傳完成后的文件地址、文件名字同步后端
          if(val.length < 1) return
          let allFileHasUpload = val.every((item, index) => {
            return item.fileGetUrl.length > 1
          })
         if(allFileHasUpload) {
           this.allFileHasUpload = allFileHasUpload
           const {modelSourceInfoId} = this
           if(modelSourceInfoId.length < 1) {
             return
           }
           const url = process.env.VUE_APP_BASE_API + "/vector-map/threeDimensionalModelService/invokeMapService"
           const files = val.map((ite, idx) => {
             return {
               	fileName: ite.file.name,
               	fileUrl: ite.fileGetUrl
             }
           })
           this.syncAllUploadedFile(url, files, modelSourceInfoId)
         }

        },
        deep: true
      }
    },

    created() {
      this.initMinioClient()
    },

    beforeDestroy() {
      if(!this.allFileHasUpload) {
        this.clearFile()
      }
    },

    methods:{
       //創(chuàng)建存儲(chǔ)桶
      async initMinioClient(){
        const { code, result, message } =  AES.decryptToJSON(await getMinioConfig({}))
        if(!result || code !== 200) {
        	this.$customMessage.error({message: '獲取存儲(chǔ)桶配置信息出錯(cuò)'})
        	return false
        }
        let {accessKey, bucketName, endPoint, secretKey} = result
        //console.log({accessKey, bucketName, endPoint, secretKey})

        let endPointStr = endPoint.split(":")[1]
        let formatPort = Number(endPoint.split(":")[2])
        let formatEndPoint = endPointStr.split('//')[1]
        this.userBucket = bucketName
        this.minioClient = new Minio.Client({
          useSSL: false,
          partSize: '20M',
          port: formatPort,
          endPoint: formatEndPoint,
          accessKey,
          secretKey
        });
        let userBucket = this.userBucket
        //userBucket只能作為字符串變量傳入,不能作為其他變量的屬性或者函數(shù)返回值,屬于Minio的一個(gè)規(guī)定
        this.minioClient.bucketExists(userBucket, (err)=> {
          if (err && err.code == 'NoSuchBucket') {
            this.minioClient.makeBucket(userBucket, 'us-east-1', (err)=> {
              if (err) {
                return console.log('創(chuàng)建存儲(chǔ)桶失敗', err)
              }
             // console.log('Bucket created successfully in "us-east-1".')
            })
          }else{
            //console.log('存儲(chǔ)桶存在')
          }
        })
      },

      close(flag = false) {
        this.$emit('close', flag)
        //關(guān)閉彈窗時(shí),如果文件沒(méi)有上傳完成,則清空文件
        if(!this.allFileHasUpload) {
          this.clearFile()
        }
      },
      selectFile() {
        let inputDOM = this.$refs.uploadInput
        inputDOM.click();
      },

      getFileSize(file){
        let fileSize = ''
        if(file.size / 1024 < 1){
          fileSize = file.size + 'B'
        }else if(file.size / 1024 /1024 < 1){
          fileSize = file.size + 'KB'
        }else if(file.size / 1024 /1024 >=1){
           fileSize = file.size + 'MB'
        }else{
        }

        return fileSize

      },

      //刪除文件,并取消正在文件的上傳
      deleteFile(fileInfo, index){
        this.httpQueue[index] && this.httpQueue[index].abort()
        this.httpQueue[index] && this.httpQueue.splice(index, 1)
        this.fileInfoList.splice(index, 1)
        this.fileList.splice(index, 1)
        this.removeRemoteFile(fileInfo)
      },

      //清空文件并取消上傳隊(duì)列
      clearFile() {
        this.fileInfoList.forEach((item, index) => {
          this.httpQueue[index] && this.httpQueue[index].abort()
          this.httpQueue[index] && this.httpQueue.splice(index, 1)
          this.removeRemoteFile(item)
        })
        this.fileInfoList = []
        this.httpQueue = []
        this.fileList = []
      },
      //刪除遠(yuǎn)程文件
      removeRemoteFile(fileInfo){
        const userBucket = this.userBucket
        const { fileUploadProgress, file} = fileInfo
        const fileName = file.name
        const complete = fileUploadProgress === '100.00%' ? true : false
        if(complete){
         this. minioClient.removeObject(userBucket, fileName, function(err) {
            if (err) {
              return console.log('Unable to remove object', err)
            }
            console.log('Removed the object')
          })
        }else{
          this.minioClient.removeIncompleteUpload(userBucket, fileName, function(err) {
            if (err) {
              return console.log('Unable to remove incomplete object', err)
            }
            console.log('Incomplete object removed successfully.')
          })
        }
      },
      //格式化文件并創(chuàng)建上傳隊(duì)列
      getAndFormatFile(){
        let files = this.$refs.uploadInput.files
        const userBucket = this.userBucket
        if(files.length > 6) {
          this.$message({
            message: `最大只能上傳6個(gè)文件`,
            type: 'warning'
          })
          return
        }
        files.forEach((file, index) => {
          if ((file.size / 1024 / 1024).toFixed(2) > 128) { //單個(gè)文件限制大小為128MB
            this.$message({
              message: `文件大小不能超過(guò)128MB`,
              type: 'warning'
            })
            return
          }
          //創(chuàng)建文件上傳的url并格式化文件信息
          this.minioClient.presignedPutObject(userBucket, file.name, 24 * 60 * 60, (err, presignedUrl) => {
            if (err) {
              this.$message({
                message: `服務(wù)器連接超時(shí)`,
                type: 'error'
              })
              return err
            }
            let fileIcon = this.getFileIcon(file)
            let fileUploadProgress = '0%'
            this.fileInfoList.push({
              file, //文件
              fileIcon, //文件對(duì)應(yīng)的圖標(biāo) className
              fileUploadProgress, //文件上傳進(jìn)度
              filePutUrl: presignedUrl, //文件上傳put方法的url
              fileGetUrl: '', //文件下載的url
            })
          })
        })
        this.fileList = [...this.fileInfoList]
      },

      //1.上傳文件到存儲(chǔ)桶
      async handleUplaod(){
        let _this = this
        if(this.fileInfoList.length < 1) {
          this.$message({
            message: `請(qǐng)先選擇文件`,
            type: 'warning'
          })
          return
        }
        //1.1先上傳文件的基本表單信息,獲取文件信息的id
        try{
          const {remark, alias} = _this.uploadFormData
          let res = await uploadModelSourceInfo({remark, serviceName: alias})
          _this.modelSourceInfoId = res.message
        }catch(error){
          if(error) {
            _this.$message({
              message: `上傳失敗,請(qǐng)檢查服務(wù)`,
              type: 'error'
            })
            return
          }
        }

        //1.2開(kāi)始將模型資源上傳到遠(yuǎn)程的存儲(chǔ)桶
        this.fileList.forEach((item, index) => {
          const {file, filePutUrl} = item
          let config = {
            file,
            filePutUrl,
            customHeader:{
              "X-FILENAME": encodeURIComponent(file.name),
              "X-Access-Token": getToken()
            },
            onUploadProgress: ({progressEvent, uploadingFile}) => {
              let progress = (progressEvent.loaded / progressEvent.total).toFixed(2)
              this.updateFileUploadProgress(uploadingFile, progress)
            },
            onUploaded: ({result, uploadedFile}) => {
              this.updateFileDownloadUrl(uploadedFile)
            },
            onError: ({error, errorFile}) => {

            }
          }

          let httpInstance = this.createUploadHttp(config)
          this.httpQueue.push(httpInstance)
        })
      },

      //1更新對(duì)應(yīng)文件的上傳進(jìn)度
      updateFileUploadProgress(uploadingFile, progress) {
        //console.log({uploadingFile, progress})
        this.fileInfoList.forEach((item, index) => {
          if(item.file.name === uploadingFile.name){
            item.fileUploadProgress = (Number(progress)*100).toFixed(2) + '%'
          }
        })
      },

      //更新上傳完成文件的下載地址
      updateFileDownloadUrl(uploadedFile){
        const userBucket = this.userBucket
        this.fileInfoList.forEach((item, index) => {
          if(item.file.name === uploadedFile.name){
            this.minioClient.presignedGetObject(userBucket, uploadedFile.name, 24*60*60, (err, presignedUrl) => {
              if (err) return console.log(err)
              item.fileGetUrl = presignedUrl
             // console.log(presignedUrl)
            })
          }
        })
      },

      //同步已上傳的文件到后端
      syncAllUploadedFile(url, files, modelSourceInfoId){
        let xhr = new XMLHttpRequest()
        xhr.onload = () => {
          if (xhr.status === 200 && xhr.status < 300 || xhr.status === 304) {
            try {
             const res = JSON.parse(xhr.responseText)
             if(res && res.code === 200){
               this.$message({
                 message: '上傳完成',
                 type: 'success'
               })
              // setTimeout(() => {
                 this.$emit('close')
                 this.fileInfoList = []
                 this.fileList = []
                 this.httpQueue = []
              // }, 1000)
             }
            } catch(error) {
             this.$message({
               message: '上傳失敗,請(qǐng)檢查服務(wù)',
               type: 'error'
             })
            }
          }
        }
        xhr.open("post", url, true)
        xhr.setRequestHeader('Content-Type', 'application/json')
        xhr.setRequestHeader('X-Access-Token', getToken())
        //將前面1.1獲取文件信息的id作為頭信息傳遞到后端
        xhr.setRequestHeader('ThreeDimensionalModel-ServiceID', modelSourceInfoId)
        xhr.send(JSON.stringify(files))
      },

      //獲取文件類(lèi)型圖標(biāo)class
      getFileIcon(file) {
        const { type } = file
        let icon = ''
        return icon
      },

      //創(chuàng)建上傳文件的http
      createUploadHttp(config){
        const {file, filePutUrl, customHeader, onUploadProgress, onUploaded, onError} = config
        let fileName = file.name
        let http = new XMLHttpRequest();
        http.upload.addEventListener("progress", (e) => {
          onUploadProgress({
            progressEvent: e,
            uploadingFile: file
          })
        }, false)

        http.onload = () => {
          if (http.status === 200 && http.status < 300 || http.status === 304) {
            try {
              const result = http.responseURL
              onUploaded({ result, uploadedFile: file})
            } catch(error) {
              onError({ error, errorFile: file})
            }
          }
        }
        http.open("PUT", filePutUrl, true);
        Object.keys(customHeader).forEach((key, index) =>{
          http.setRequestHeader(key, customHeader[key])
        })
        http.send(file);
        return http
      }

    }
  }
</script>

<style scoped lang="scss">

  .header-title {
  //  height: 24px;
    border-bottom: 1px solid #EFEFEF;
    padding-bottom: 20px;
    //outline: 1px solid red;
    .icon {
      width: 26px;
      height: 26px;
      background-image: url(../images/icon_upload.png);
      float: left;
      background-size: 100% 100%;
      margin-right: 18px;
      margin-left: 10px;
    }
    span {
      font-size: 23px;
      font-family: Source Han Sans CN;
      font-weight: 500;
      color: #333333;
    }
    i {
      float: right;
      font-size: 16px;
      color: rgba(176, 176, 176, 1);
      cursor: pointer;
      &:hover {
        color: #0069D5 ;
      }
    }
  }
  .tip {
    font-size: 12px;
    display: block;
  }
  .file-list {
    box-sizing: border-box;
    padding: 5px;
   // border: 1px solid red;
   // width: 840px;
    margin: 5px auto;
    margin-left: 80px;
    .file-item {
      min-height: 32px;
      display: flex;
      justify-content: flex-start;
      align-items: center;
      font-size: 12px;
      div {
        margin-right: 15px;
        text-align: left;
      }
      .name {
        width: 200px;
      }
      .size {
        width: 60px;
      }
      .progress {
        width: 180px;
        height: 8px;
        border-radius: 4px;
        background-color: #E2E2E2;
        .bar {
          width: 50%;
          height: 8px;
          border-radius: 4px;
          background-color: #13A763;
        }
      }
      .rate {
        width: 60px;
       // border: 1px solid red;

      }
      .delete-btn {
        cursor: pointer;
        font-size: 16px;
      }
    }
  }

  .custom-footer {
   // border: 1px solid red;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    width: 100%;
    height: 80px;
    background-color: #fff;
    //box-shadow: 0px 1px 0px 0px red;
    border-top: 1px solid #efefef;
    z-index: 10;
    button {
      width: 90px;
      height: 40px;
      border-radius: 4px;
      border: 0;
      font-size: 16px;
      font-family: Source Han Sans CN;
      &:focus {
        border: 0;
        outline: 0;
      }

      &.info {
        color: #8f8f8f;
        background: #e2e2e2;
        &:hover{
          background-color: #A6A9AD;
          cursor: pointer;
          color: #fff;
        }
      }
      &.success {
        background: #12a763;
        color: #fff;
        &:hover{
          cursor: pointer;
          background-color: #73C132;
        }
      }
    }
  }
</style>

源碼分享

其實(shí)一開(kāi)始我是想提供一個(gè)demo的,發(fā)現(xiàn)這個(gè)東西與產(chǎn)品功能強(qiáng)綁定,沒(méi)有測(cè)試的服務(wù)地址和存儲(chǔ)桶,也無(wú)法做出一個(gè)開(kāi)放的案例

所以上面的代碼只是給大家提供一種實(shí)現(xiàn)方式和思路,里面具體的細(xì)節(jié)處理我做的比較復(fù)雜

總結(jié)

到此這篇關(guān)于vue+Minio實(shí)現(xiàn)多文件進(jìn)度上傳的文章就介紹到這了,更多相關(guān)vue+Minio多文件進(jìn)度上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue3.x對(duì)echarts的二次封裝之按需加載過(guò)程詳解

    vue3.x對(duì)echarts的二次封裝之按需加載過(guò)程詳解

    echarts是我們后臺(tái)系統(tǒng)中最常用的數(shù)據(jù)統(tǒng)計(jì)圖形展示,外界對(duì)它的二次封裝也不計(jì)層數(shù),這篇文章主要介紹了vue3.x對(duì)echarts的二次封裝之按需加載,需要的朋友可以參考下
    2023-09-09
  • Vue實(shí)現(xiàn)動(dòng)態(tài)路由導(dǎo)航的示例

    Vue實(shí)現(xiàn)動(dòng)態(tài)路由導(dǎo)航的示例

    本文主要介紹了Vue實(shí)現(xiàn)動(dòng)態(tài)路由導(dǎo)航的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Vue整合AdminLTE模板的方法

    Vue整合AdminLTE模板的方法

    這篇文章主要介紹了Vue整合AdminLTE模板的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 解決vue打包項(xiàng)目后刷新404的問(wèn)題

    解決vue打包項(xiàng)目后刷新404的問(wèn)題

    下面小編就為大家整理了一篇解決vue打包項(xiàng)目后刷新404的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • vue使用axios跨域請(qǐng)求數(shù)據(jù)問(wèn)題詳解

    vue使用axios跨域請(qǐng)求數(shù)據(jù)問(wèn)題詳解

    這篇文章主要為大家詳細(xì)介紹了vue使用axios跨域請(qǐng)求數(shù)據(jù)的問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • vue代理模式解決跨域詳解

    vue代理模式解決跨域詳解

    這篇文章主要介紹了vue代理模式解決跨域詳解的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • VueX瀏覽器刷新如何實(shí)現(xiàn)保存數(shù)據(jù)

    VueX瀏覽器刷新如何實(shí)現(xiàn)保存數(shù)據(jù)

    這篇文章主要介紹了VueX瀏覽器刷新如何實(shí)現(xiàn)保存數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue之proxyTable代理超全面配置流程

    vue之proxyTable代理超全面配置流程

    這篇文章主要介紹了vue之proxyTable代理超全面配置流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue quill editor 使用富文本添加上傳音頻功能

    vue quill editor 使用富文本添加上傳音頻功能

    vue-quill-editor 是vue項(xiàng)目中常用的富文本插件,其功能能滿(mǎn)足大部分的項(xiàng)目需求。這篇文章主要介紹了vue-quill-editor 富文本添加上傳音頻功能,需要的朋友可以參考下
    2020-01-01
  • vuex的使用步驟

    vuex的使用步驟

    這篇文章主要介紹了vuex的使用步驟,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-01-01

最新評(píng)論