最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)瀏覽器大文件上傳的示例詳解

 更新時(shí)間:2024年07月02日 09:24:17   作者:BLACK595  
文件上傳是許多項(xiàng)目都有的功能,用戶上傳小文件速度一般都很快,但如果是大文件幾個(gè)g,幾十個(gè)g的時(shí)候,上傳了半天,馬上就要完成的時(shí)候,網(wǎng)絡(luò)波動(dòng)一下,文件又要重新上傳,所以本文給大家介紹了Java實(shí)現(xiàn)瀏覽器大文件上傳的示例,需要的朋友可以參考下

前言

文件上傳是許多項(xiàng)目都有的功能,用戶上傳小文件速度一般都很快,但如果是大文件幾個(gè)g,幾十個(gè)g的時(shí)候,上傳了半天,馬上就要完成的時(shí)候,網(wǎng)絡(luò)波動(dòng)一下,文件又要重新上傳,抓狂。那有什么辦法解決解決這個(gè)問(wèn)題,答案就是把文件分片,一段一段把文件拆開(kāi)上傳。

核心講解

原理

分片上傳:把一個(gè)完整的文件,前端把文件分成多個(gè)小塊的chunk,一塊一塊的傳遞給后端,后端接收到后再把全部的塊拼接起來(lái),這樣就算在某個(gè)時(shí)間點(diǎn)發(fā)生網(wǎng)絡(luò)波動(dòng),那么丟失的也只有一塊。

秒傳:前端在把文件分片前,先計(jì)算出文件的md5值,后端拿到這個(gè)md5先去檢查下是否已經(jīng)有這個(gè)文件了,如果有直接給前端上傳成功。這就是我們?cè)诰W(wǎng)盤(pán)上有時(shí)候出現(xiàn)的文件秒傳,說(shuō)明已經(jīng)有人跟你上傳過(guò)同一份文件了。

斷點(diǎn)續(xù)傳:當(dāng)網(wǎng)絡(luò)出現(xiàn)異常上傳中斷后我們繼續(xù)上傳時(shí),先去后端請(qǐng)求接口,拿到已經(jīng)上傳過(guò)的分片下標(biāo),再繼續(xù)上傳沒(méi)有上傳的分片。

整體流程

  • 用戶選擇文件進(jìn)行上傳
  • 前端獲取文件唯一標(biāo)識(shí)md5
  • 判斷文件md5是否已經(jīng)保存,是則秒傳
  • 判斷文件分片是否已經(jīng)上傳部分,是則斷點(diǎn)續(xù)傳
  • 上傳分片文件
  • 后端合并分片
  • 分片上傳完成

功能分析

前端

前端實(shí)現(xiàn)的功能難點(diǎn)在于文件分片,和獲取文件的md5。

文件分片

因?yàn)閖s的File對(duì)象繼承自Blob,所以他也有slice方法,slice方法需要的參數(shù)有兩個(gè),一個(gè)是startByte文件起始讀取的字節(jié)位置,另一個(gè)是endByte結(jié)束讀取的字節(jié)位置。

let fileChunkList = []; //存放文件切片
let cur = 0;
// 分片
while(cur < file.size){
  fileChunkList.push(file.slice(cur,cur + chunkSize));
  cur += chunkSize;
}

獲取文件md5

獲取文件的md5,推薦使用SparkMD5的文件增量方式獲取,如果直接計(jì)算文件的hash,文件過(guò)大時(shí)對(duì)瀏覽器負(fù)擔(dān)會(huì)較大。

上傳文件

通過(guò)check接口上傳前先判斷是否秒傳和獲取已經(jīng)上傳的分片下標(biāo)。

function handleBeforeUpload(file) {
  const chunkSize = 1024 * 1024 * 10; // 10MB
  // 計(jì)算md5
  md5(file, chunkSize).then(md5 => {
    //檢查是否秒傳
    request({
      url: "/upload/check/" + md5,
      method: "get",
    }).then(result => {
      const isOk = result.isOk;
      const haveList2 = result.haveList; //已經(jīng)上傳的分片下標(biāo)
      if(isOk) {
        console.log("秒傳成功");
        return;
      }
      haveList.value = haveList2;
      let chunkIndex = 0;
      //上傳第一個(gè)分片
      upload(fileChunkList.value, chunkIndex, md5, file);
    })
  });
  return false;
}

已經(jīng)上傳的這些分片下標(biāo)要跳過(guò)上傳

后端

分片來(lái)后端后,使用RandomAccessFile就可以在一個(gè)文件上進(jìn)行操作,而不用使用創(chuàng)建多個(gè)臨時(shí)文件最后合并的方式,通過(guò)分片下標(biāo)和分片大小計(jì)算出偏移量,使用RandomAccessFile將跳到偏移開(kāi)始位置存放數(shù)據(jù)。RandomAccessFile的第二個(gè)參數(shù)的model有如下;

? "r":以只讀方式打開(kāi)指定文件。 ? "rw":以讀、寫(xiě)方式打開(kāi)指定文件。 ? "rws":以讀、寫(xiě)方式打開(kāi)指定文件。相對(duì)于"rw"模式,還要求對(duì)文件的內(nèi)容或元數(shù)據(jù)的每個(gè)更新都同步寫(xiě)入到底層存儲(chǔ)設(shè)備。 ? "rwd":以讀、寫(xiě)方式打開(kāi)指定文件。相對(duì)于"rw"模式,還要求對(duì)文件內(nèi)容的每個(gè)更新都同步寫(xiě)入到底層存儲(chǔ)設(shè)備。

/**
 * 分片文件上傳
 * @param file 文件
 * @param chunkIndex 分片下標(biāo)
 * @param md5 md5
 * @param totalFileSize 文件總大小
 * @param fileName 文件名
 */
@PostMapping("/shard")
public AjaxResult shardUpload(@RequestParam MultipartFile file, @RequestParam Integer chunkIndex,
                              @RequestParam String md5, @RequestParam Long totalFileSize,
                              @RequestParam String fileName) throws Exception{
    // 存放文件目錄
    String dirPath = System.getProperty("user.dir") + "/file/"+md5+"/";
    File dirFile = new File(dirPath);
    if(!dirFile.exists()){
        dirFile.mkdir();
    }
    File tempFile = new File(dirPath + fileName);
    RandomAccessFile rw = new RandomAccessFile(tempFile, "rw");
    // 定位到分片的偏移量
    rw.seek(CHUNK_SIZE * chunkIndex);
    // 寫(xiě)入分片數(shù)據(jù)
    rw.write(file.getBytes());
    // 關(guān)閉流
    rw.close();
    // 讀取已經(jīng)分片集合
    List<Object> hasChunkList;
    String hasChunkKey = CHUNK_PREFIX + md5;
    if(redisCache.hasKey(hasChunkKey)){
        hasChunkList = redisCache.getCacheList(hasChunkKey);
    } else {
        hasChunkList = new ArrayList<>();
    }
    hasChunkList.add(chunkIndex);
    // 將最新的分片下標(biāo)更新到Redis中
    redisCache.addCacheListOne(hasChunkKey,chunkIndex);
    // 判斷是否上傳完成
    int totalNeedChunks = (int) Math.ceil((double) totalFileSize / CHUNK_SIZE);
    // 總共需要的分片數(shù) 和 已經(jīng)分片上傳的數(shù)量相等 則上傳完成
    boolean isOk = totalNeedChunks == hasChunkList.size();
    if(isOk){
        redisCache.setCacheObject(UPLOAD_ISOK_PREFIX + md5, true);
    }
    AjaxResult ajax = AjaxResult.success();
    ajax.put("hasChunkList",hasChunkList);
    ajax.put("isOk",isOk);
    return ajax;
}

最終演示

上傳完成演示

秒傳演示

斷點(diǎn)演示

待優(yōu)化

  • 提供查詢進(jìn)度接口,前端進(jìn)度條展示,增加用戶體驗(yàn)。
  • 多線程上傳,不同分片用多線程,提高下載速度。

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

相關(guān)文章

  • Java為圖片添加水印并保存實(shí)現(xiàn)方法(附帶源碼)

    Java為圖片添加水印并保存實(shí)現(xiàn)方法(附帶源碼)

    這篇文章主要介紹了如何使用Java編程語(yǔ)言在圖像上添加文字或圖片水印,并提供了一個(gè)簡(jiǎn)單的Java程序?qū)崿F(xiàn),文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2025-03-03
  • Java中I/O輸入輸出的深入講解

    Java中I/O輸入輸出的深入講解

    Java的I/O技術(shù)可以將數(shù)據(jù)保存到文本文件、二進(jìn)制文件甚至是ZIP壓縮文件中,以達(dá)到永久性保存數(shù)據(jù)的要求,下面這篇文章主要給大家介紹了關(guān)于Java中I/O輸入輸出的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • SpringBoot接收前端參數(shù)的幾種常用方式

    SpringBoot接收前端參數(shù)的幾種常用方式

    在Spring Boot開(kāi)發(fā)中接收參數(shù)是非常常見(jiàn)且重要的一部分,依賴于請(qǐng)求的不同場(chǎng)景,Spring Boot提供了多種方式來(lái)處理和接收參數(shù),這篇文章主要給大家介紹了關(guān)于SpringBoot接收前端參數(shù)的幾種常用方式,需要的朋友可以參考下
    2024-07-07
  • java解析背包問(wèn)題實(shí)例代碼(簡(jiǎn)單易懂)

    java解析背包問(wèn)題實(shí)例代碼(簡(jiǎn)單易懂)

    背包問(wèn)題是計(jì)算機(jī)科學(xué)中的經(jīng)典問(wèn)題,涉及到在給定的一組物品中選擇最佳的物品組合,以使其總重量不超過(guò)背包所能承載的限制,同時(shí)獲得最大的總價(jià)值,這篇文章主要介紹了java解析背包問(wèn)題的相關(guān)資料,需要的朋友可以參考下
    2025-10-10
  • springboot讀取application.yaml文件數(shù)據(jù)的方法

    springboot讀取application.yaml文件數(shù)據(jù)的方法

    這篇文章主要為大家詳細(xì)介紹了springboot讀取application.yaml文件數(shù)據(jù)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制

    Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制

    三級(jí)緩存機(jī)制是Spring解決循環(huán)依賴問(wèn)題的關(guān)鍵,本文主要介紹了Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-02-02
  • JAVA調(diào)用JavaScript方法舉例詳解

    JAVA調(diào)用JavaScript方法舉例詳解

    之前在一次機(jī)緣巧合的情況下,需要時(shí)用JAVA執(zhí)行js方法,查閱了一些文檔,找到了相關(guān)解決方法,這里和大家分享一下,下面這篇文章主要給大家介紹了關(guān)于JAVA調(diào)用JavaScript方法的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • SpringBoot+Mybatis plus實(shí)現(xiàn)多數(shù)據(jù)源整合的實(shí)踐

    SpringBoot+Mybatis plus實(shí)現(xiàn)多數(shù)據(jù)源整合的實(shí)踐

    本文主要介紹了SpringBoot+Mybatis plus實(shí)現(xiàn)多數(shù)據(jù)源整合的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Spring Boot啟動(dòng)流程示例詳解

    Spring Boot啟動(dòng)流程示例詳解

    Spring Boot是Spring官方推出的一個(gè)快速構(gòu)建獨(dú)立運(yùn)行、生產(chǎn)級(jí)別Spring應(yīng)用的框架,它的核心目標(biāo)是簡(jiǎn)化Spring應(yīng)用的搭建與部署,本文給大家介紹Spring Boot啟動(dòng)流程詳解,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • JDK版本管理工具jEnv解決不同jdk版本項(xiàng)目

    JDK版本管理工具jEnv解決不同jdk版本項(xiàng)目

    本文主要介紹了JDK版本管理工具jEnv解決不同jdk版本項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論

来宾市| 德昌县| 泸水县| 东阿县| 慈利县| 荔浦县| 孝感市| 五大连池市| 江永县| 美姑县| 永泰县| 都安| 永福县| 天祝| 汝州市| 临泉县| 迭部县| 郑州市| 东阳市| 山东| 杭锦后旗| 万山特区| 胶南市| 龙里县| 治多县| 冀州市| 新巴尔虎右旗| 常山县| 博客| 柯坪县| 观塘区| 安平县| 高要市| 隆安县| 兴安盟| 台南县| 望都县| 沧源| 石家庄市| 屏山县| 敦化市|