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

Vue?+?SpringBoot?實現(xiàn)文件的斷點上傳、秒傳存儲到Minio的操作方法

 更新時間:2024年06月20日 15:04:51   作者:魚蝦一整碗?  
這篇文章主要介紹了Vue?+?SpringBoot?實現(xiàn)文件的斷點上傳、秒傳存儲到Minio的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

一、前端

1. 計算文件的md5值

  前端頁面使用的elment-plus的el-upload組件。

    <el-upload action="#" :multiple="true" :auto-upload="false" :on-change="handleChange" :show-file-list="false">
      <FileButton content="上傳文件" type="primary" class="file-button" />
    </el-upload>

當(dāng)上傳文件后,會調(diào)用handleChange 方法,可以在這里進(jìn)行文件相關(guān)的操作。

//處理文件上傳
const handleChange = async (uploadFile) => {
  //文件名字
  let fileName = uploadFile.name
  //文件的大小
  const fileSize = uploadFile.size || 0
  //當(dāng)前的文件對象
  let fileItem = {}
  fileItem.fileName = fileName
  fileItem.fileSize = fileSize
  fileItem.state = 1  //解碼中
  fileItem.progress = 0  //進(jìn)度是0
  fileItem.filePid = 102903232
  fileItem.fileMd5 = ""
  fileItem.uploadSize = 0
  fileUploadList.value.addFile(fileItem)
  //彈框顯示
  isVisible.value = true
  //獲得文件的md5
  if (uploadFile.raw) {
    await generateMD5OfFile(uploadFile.raw).then(
      res => {
        fileItem.fileMd5 = res
      }
    )
  }
  fileUploadList.value.addMd5(fileItem.fileName, fileItem.fileMd5)
  fileUploadList.value.changeFileState(fileItem.fileName, 2)
  //分片上傳
  let chunkTotals = Math.ceil(fileSize / chunkSize);
  //分片上傳
  if (chunkTotals > 0) {
    for (let chunkNumber = 0, start = 0; chunkNumber < chunkTotals; chunkNumber++, start += chunkSize) {
      //文件最后的end
      let end = Math.min(fileSize, start + chunkSize);
      // el-mement - plus中,上傳的文件就在raw里面
      const files = uploadFile.raw?.slice(start, end)
      //上傳的結(jié)果
      const result = await uploadFileToServer(files, chunkNumber + 1, chunkTotals, fileName , getCurrentId(), fileItem.fileMd5,userId)
      console.log(result.data)
      console.log(result.data.data)
      if (result.data.data.status === 1) {
        // console.log("上傳中")
        //上傳的進(jìn)度
        fileUploadList.value.changeProgress(fileItem.fileName, ((end / fileSize) * 100).toFixed(1))
        //修改已經(jīng)上傳完成的文件大小
        fileUploadList.value.changeUploadSize(fileItem.fileName, end)
      } else if (result.data.data.status === 3) {
        // console.log("上傳成功!"),這里是彈窗顯示的文件上傳進(jìn)度,可以適當(dāng)修改
        fileUploadList.value.changeFileState(fileItem.fileName, 3)  //上傳完成
        fileUploadList.value.changeProgress(fileItem.fileName, 100)  // 進(jìn)度100%
        //通過main,進(jìn)行刷新
        $emit("addChangeNum")
        return ; //結(jié)束
      } else {
        message("上傳失敗", 'error')
        return;  //結(jié)束
      }
    }
  }
}

 計算文件的MD5值

//計算文件的md5
function generateMD5OfFile(file) {
    return new Promise((resolve, reject) => {
        let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,                        // Read in chunks of 2MB
            chunks = Math.ceil(file.size / chunkSize),
            currentChunk = 0,
            spark = new SparkMD5.ArrayBuffer(),
            fileReader = new FileReader();
        fileReader.onload = function (e) {
            console.log('read chunk nr', currentChunk + 1, 'of', chunks);
            spark.append(e.target.result);                   // Append array buffer
            currentChunk++;
            if (currentChunk < chunks) {
                loadNext();
            } else {
                resolve(spark.end())
            }
        };
        fileReader.onerror = function () {
            reject('MD5 calc error')
        };
        function loadNext() {
            let start = currentChunk * chunkSize,
                end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
            fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        }
        loadNext();
    })
}

2.計算文件切片數(shù)量

自定義文件切片大小 

//默認(rèn)分片大小
const chunkSize = 5 * 1024 * 1024

3.分片上傳文件

上傳文件到服務(wù)器 

// 上傳文件到服務(wù)器
const uploadFileToServer = async (file, chunkNumber, chunkTotal, fileName,filePid, fileMd5,userId) => {
    const form = new FormData();
    // 這里的data是文件
    form.append("file", file);
    form.append("chunkNumber", chunkNumber);
    form.append("chunkTotal", chunkTotal);
    form.append("fileName", fileName)
    form.append("fileMd5", fileMd5)
    form.append("filePid", filePid)
    form.append("userId", userId)
    var result = await axios({
        url: env_server_production + '/file/upload',
        headers: { 'Content-Type': 'multipart/form-data' },
        method: "post",
        timeout: 1000000,
        data: form
    })
    return result
}

4.實現(xiàn)相關(guān)文件的預(yù)覽

可以簡單的實現(xiàn)對一些文件的預(yù)覽,比如圖片、視頻、word、pdf等等。

pdf:

等等

這里使用的是vue-office 

<template>
  <div class="preview-body">
    <!-- word -->
    <vue-office-docx v-if="getFileType() == 1" :src="getFileUrl()" style="height: 400px;" @rendered="renderedHandler"
      @error="errorHandler" />
    <!-- pdf -->
    <vue-office-pdf v-else-if="getFileType() == 2" :src="getFileUrl()" style="height: 400px;"
      @rendered="renderedHandler" @error="errorHandler" />
    <!-- iamge -->
    <div v-else-if="getFileType() == 3">
      <el-image :src="getFileUrl()" style="height: 100px; width: 100px;" :zoom-rate="1.2" :max-scale="7"
        :min-scale="0.2" :preview-src-list="imageList" :initial-index="4" />
      <br>
      <el-text style="margin-left: 0px;" link type="primary">點擊圖片查看詳情</el-text>
    </div>
    <!-- 不支持顯示 -->
    <div v-else-if="getFileType() == 4">
      <br>
      該文件不支持在線瀏覽,請下載后查看!
    </div>
    <!-- 視頻 -->
    <div v-else-if="getFileType() == 5">
        <video autoplay width="1200px" height="400px" controls 
          :src="getFileUrl()"
          id="myVideo"
          >
        </video>
    </div>
    <!-- 文本顯示 -->
    <div v-else>
      <el-scrollbar height="400px" class="document-preview">
        <pre>{{ documentContent }}</pre>
      </el-scrollbar>
    </div>
  </div>
</template>
<script setup>
//引入相關(guān)樣式
import VueOfficeDocx from '@vue-office/docx'
import VueOfficePdf from '@vue-office/pdf'
import '@vue-office/docx/lib/index.css'
import { ref } from 'vue'
import axios from 'axios';
const props = defineProps(['file'])
const video = document.getElementById("myVideo")
const getFileUrl = () => {
  return "http://60.205.141.200:9000/" + props.file.filePath;
}
const getFileType = () => {
  let category = props.file.fileCategory
  if (category == 18 || category == 19) {
    return 1
  }
  else if (category == 13)
    return 2
  else if (category == 9 || category == 14 || category == 5) {
    imageList.value.push(getFileUrl())
    return 3
  }
  else if (category == 20 || category == 11 || category == 15)
    return 4
  else if (category == 12) {
    //視頻
    return 5
  } else {
    //文本
    readDocumentContent();
  }
}
const readDocumentContent = async () => {
  var res = await axios.get(getFileUrl(), {
    responseType: 'text',
  })
  documentContent.value = `\n${res.data}\n`
}
//文件中的內(nèi)容
const documentContent = ref('')
//圖片列表
const imageList = ref([])
const renderedHandler = () => {
  console.log("渲染成功")
}
const errorHandler = () => {
  console.log("渲染失敗")
}
</script>
<style lang="scss" scoped>
.document-preview {
  margin-right: 100px;
  background-color: #ccc;
  width: 1164px;
  border: 2px solid #ccc;
  height: 400px;
  border-radius: 0 0 10px 10px;
  text-align: left;
}
pre {
  font-family: 'Microsoft YaHei';
}
</style>

二、后端

后端使用minio,minio先接收分片文件,上傳完成所有的分片文件后,在合并分片文件,刪除中間文件即可。

1.接收分片文件、合并文件。

/**
     * 上傳文件方法。
     * 該方法負(fù)責(zé)檢查文件是否已存在,如果存在,則返回已存在標(biāo)志;如果不存在且是完整文件,則上傳文件到MinIO并保存文件信息到數(shù)據(jù)庫。
     *
     * @param fileVO 文件相關(guān)信息VO,包含文件本身、MD5、文件名等。
     * @return 如果文件已存在,返回秒傳狀態(tài)碼;如果文件上傳完成,返回上傳完成狀態(tài)碼;否則返回null。
     * @throws GeneralException 如果文件為空,拋出通用異常。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)  //所有的操作都在一個事務(wù)里面。
    public HashMap<Object, Object> uploadFile(FileVO fileVO) {
        if(fileVO.getFile().isEmpty())
            throw  new GeneralException("文件上傳異常");
        FileInfo insertItem = new FileInfo();
        Date now = new Date();
        HashMap<Object, Object> map = new HashMap<>();
        //第一片文件
        if(fileVO.getChunkNumber() == 1){
            //先去數(shù)據(jù)庫看看有沒有這個文件
            QueryWrapper<FileInfo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("file_md5", fileVO.getFileMd5());
            //通過Md5查詢,別人是不是已經(jīng)傳過這個文件了(文件名不影響文件的MD5值)。
            List<FileInfo> fileInfoList = fileInfoMapper.selectList(queryWrapper);
            FileInfo fileInfo = null;
            if(fileInfoList.size() > 0){
                fileInfo = fileInfoList.get(0);
            }
            //別人已經(jīng)上傳過這個文件了,直接秒傳
            if(fileInfo != null){
                log.info("服務(wù)器中有相同的文件,直接秒傳");
                //說明minIO中有對應(yīng)的文件
                insertItem.setUserId(fileVO.getUserId());
                insertItem.setFileMd5(fileVO.getFileMd5());
                insertItem.setFileName(fileInfo.getFileName());
                insertItem.setFileCategory(fileInfo.getFileCategory());
                insertItem.setFileId(StringUtil.getRandomString(10));
                insertItem.setDelFlag(FileDelFlagEnums.USING.getFlag());
                insertItem.setFilePid(fileVO.getFilePid());
                insertItem.setFilePath(fileInfo.getFilePath());
                insertItem.setCreateTime(now);
                insertItem.setFileSize(fileInfo.getFileSize());
                insertItem.setState(UploadStatus.UPLOAD_FINISH.getStatus());
                fileInfoMapper.insert(insertItem);
                System.err.println(insertItem);
                map.put("status",UploadStatus.UPLOAD_FINISH.getStatus());
                map.put("fileId",insertItem.getFileId());
                return map;
            }
            //插入 一個切片
            redisUtil.set(fileVO.getFileMd5(),0);
        }
        if(Integer.parseInt(redisUtil.get(fileVO.getFileMd5()).toString()) >= fileVO.getChunkNumber()){
            //說明這片文件已經(jīng)上傳過了。
            map.put("status",UploadStatus.UPLOADING.getStatus());
            return map;
        }
        //只有一段,直接放到服務(wù)器就行
        if(fileVO.getChunkTotal() == 1){
            int lastDotIndex = fileVO.getFileName().lastIndexOf(".");
            String type = fileVO.getFileName().substring(lastDotIndex + 1);
            String url = minioUtils.uploadFile(MessageConstant.MINIO_BUCKET,fileVO.getFileName(), fileVO.getFile());
            insertItem.setUserId(fileVO.getUserId());
            insertItem.setFileMd5(fileVO.getFileMd5());
            insertItem.setFileName(fileVO.getFileName());
            insertItem.setFileCategory(FileCategoryEnums.getByCode(type).getCategory());
            insertItem.setFileId(StringUtil.getRandomString(10));
            insertItem.setDelFlag(FileDelFlagEnums.USING.getFlag());
            insertItem.setFilePid(fileVO.getFilePid());
            insertItem.setFilePath(url);
            insertItem.setCreateTime(now);
            insertItem.setFileSize(fileVO.getFile().getSize());
            insertItem.setState(UploadStatus.UPLOAD_FINISH.getStatus());
            fileInfoMapper.insert(insertItem);
            //刪除redis中的切片上傳信息
            redisUtil.del(fileVO.getFileMd5());
            map.put("status",UploadStatus.UPLOAD_FINISH.getStatus());
            map.put("fileId",insertItem.getFileId());
            return map;
        }
        log.info("分片上傳====> md5 :{} ,=====> index :{}",fileVO.getFileMd5(),fileVO.getChunkNumber());
        //不止一片,繼續(xù)上傳
        //放切片文件的目錄是 文件的userId + md5值,這個是唯一的。
        String objectName = fileVO.getUserId() + fileVO.getFileMd5() ;
        try {
            minioUtils.putChunkObject(fileVO.getFile().getInputStream(), MessageConstant.MINIO_BUCKET, objectName + "/" + fileVO.getChunkNumber());
        } catch (IOException e) {
            throw new GeneralException("文件上傳異常!");
        }
        //最后一片,進(jìn)行合并
        if(Objects.equals(fileVO.getChunkNumber(), fileVO.getChunkTotal())){
            //獲得文件類型
            int lastDotIndex = fileVO.getFileName().lastIndexOf(".");
            String type = fileVO.getFileName().substring(lastDotIndex + 1);
            //objectName : userId+md5
            String filePath = minioUtils.composeObject(MessageConstant.MINIO_BUCKET,MessageConstant.MINIO_BUCKET,objectName, type);
            insertItem.setUserId(fileVO.getUserId());
            insertItem.setFileMd5(fileVO.getFileMd5());
            insertItem.setFileName(fileVO.getFileName());
            insertItem.setFileCategory(FileCategoryEnums.getByCode(type).getCategory());
            insertItem.setFileId(StringUtil.getRandomString(10));
            insertItem.setDelFlag(FileDelFlagEnums.USING.getFlag());
            insertItem.setFilePid(fileVO.getFilePid());
            insertItem.setFilePath(filePath);
            insertItem.setCreateTime(now);
            Long fileSize = MessageConstant.DEFAULT_CHUNK_SIZE * (fileVO.getChunkTotal() - 1) + fileVO.getFile().getSize();
            insertItem.setFileSize(fileSize);
            insertItem.setState(UploadStatus.UPLOAD_FINISH.getStatus());
            //插入一條數(shù)據(jù)
            System.out.println(fileInfoMapper.insert(insertItem));
            //刪除minio中的臨時文件目錄
            System.out.println(minioUtils.deleteFolder(MessageConstant.MINIO_BUCKET, objectName));
            //刪除redis中的切片上傳信息
            redisUtil.del(fileVO.getFileMd5());
            map.put("status",UploadStatus.UPLOAD_FINISH.getStatus());
            map.put("fileId",insertItem.getFileId());
            return map;
        }
        //更新redis中的切片上傳信息
        redisUtil.incrby(fileVO.getFileMd5(),1);
        //上傳中
        map.put("status",UploadStatus.UPLOADING.getStatus());
        return map;
    }

如何做到秒傳?

一個文件有個不重復(fù)的md5值,所謂的秒傳其實就是你要上傳的文件,別人已經(jīng)上傳過了,minio中已經(jīng)有這個文件了,再解析完文件的md5值之后,后端發(fā)現(xiàn)數(shù)據(jù)庫中md5存在了,所以就不用上傳文件了,直接在數(shù)據(jù)庫中創(chuàng)建一個信息即可,也就實現(xiàn)了秒傳。

如何做到斷點傳遞?

傳統(tǒng)傳遞過程是一整個文件上傳,如果中斷了下次傳的時候,需要重新上傳;斷點傳遞,每次傳遞的時候,可以把分片信息放到redis中,同時下一次傳分片的時候,判斷一下,redis中時候已經(jīng)有了這個分片,如果有就不用上傳此分片文件,即斷點傳遞。

到此這篇關(guān)于Vue + SpringBoot 實現(xiàn)文件的斷點上傳、秒傳,存儲到Minio的文章就介紹到這了,更多相關(guān)SpringBoot斷點上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vant list組件滾動保留滾動條位置

    vant list組件滾動保留滾動條位置

    這篇文章主要介紹了vant list組件滾動保留滾動條位置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Vue項目使用localStorage+Vuex保存用戶登錄信息

    Vue項目使用localStorage+Vuex保存用戶登錄信息

    這篇文章主要為大家詳細(xì)介紹了Vue項目使用localStorage+Vuex保存用戶登錄信息,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • VUE使用axios調(diào)用后臺API接口的方法

    VUE使用axios調(diào)用后臺API接口的方法

    這篇文章主要介紹了VUE使用axios調(diào)用后臺API接口的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-08-08
  • VUE側(cè)邊導(dǎo)航欄實現(xiàn)篩選過濾的示例代碼

    VUE側(cè)邊導(dǎo)航欄實現(xiàn)篩選過濾的示例代碼

    本文主要介紹了VUE側(cè)邊導(dǎo)航欄實現(xiàn)篩選過濾的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 關(guān)于Vue?"__ob__:Observer"屬性的解決方案詳析

    關(guān)于Vue?"__ob__:Observer"屬性的解決方案詳析

    在操作數(shù)據(jù)的時候發(fā)現(xiàn),__ob__: Observer這個屬性出現(xiàn)之后,如果單獨拿數(shù)據(jù)的值,就會返回undefined,下面這篇文章主要給大家介紹了關(guān)于Vue?"__ob__:Observer"屬性的解決方案,需要的朋友可以參考下
    2022-11-11
  • vue-router+vuex addRoutes實現(xiàn)路由動態(tài)加載及菜單動態(tài)加載

    vue-router+vuex addRoutes實現(xiàn)路由動態(tài)加載及菜單動態(tài)加載

    本篇文章主要介紹了vue-router+vuex addRoutes實現(xiàn)路由動態(tài)加載及菜單動態(tài)加載,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • vue 對axios get pust put delete封裝的實例代碼

    vue 對axios get pust put delete封裝的實例代碼

    在本篇文章里我們給各位整理的是一篇關(guān)于vue 對axios get pust put delete封裝的實例代碼內(nèi)容,有需要的朋友們可以參考下。
    2020-01-01
  • Vue2.0 UI框架ElementUI使用方法詳解

    Vue2.0 UI框架ElementUI使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Vue2.0 UI框架ElementUI的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • 如何封裝Vue Element的table表格組件

    如何封裝Vue Element的table表格組件

    這篇文章主要介紹了如何封裝Vue Element的table表格組件,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下
    2021-02-02
  • 淺談Vue開發(fā)人員的7個最好的VSCode擴(kuò)展

    淺談Vue開發(fā)人員的7個最好的VSCode擴(kuò)展

    這篇文章主要介紹了淺談Vue開發(fā)人員的7個最好的VSCode擴(kuò)展,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評論