java對大文件分片上傳

  • 這里記錄一下,Java對大文件的切分,和后端接口分片上傳的實現邏輯
    正常,前后端分離的項目其實是前端去切分文件,后端接口接收到切分后的分片文件去合并,這里都用java來記錄一下。
  • 特別說明:我這里用的是zip包的上傳,里面是音頻文件,如果你的文件是單個文件,切分和合并文件邏輯都是一樣的,只是不用后續的解壓。
  • 因為是測試代碼,所以部分代碼規范不嚴謹

1.文件的切分

public static void chunkFile(){//每片的大小,這里是10Mint TENMB = 10485760;//要切分的大文件和切分后的文件放到目錄String PATH = "D:\\test\\fenpian\\";try {File file = new File(PATH, "55.zip");RandomAccessFile accessFile = new RandomAccessFile(file, "r");// 文件的大小long size = FileUtil.size(file);int chunkSize = (int) Math.ceil((double) size / TENMB);for (int i = 0; i < chunkSize; i++) {// 文件操作的指針位置long filePointer = accessFile.getFilePointer();byte[] bytes;if (i == chunkSize - 1) {int len = (int) (size - filePointer);bytes = new byte[len];accessFile.read(bytes, 0, bytes.length);} else {bytes = new byte[TENMB];accessFile.read(bytes, 0, bytes.length);}FileUtil.writeBytes(bytes, new File(PATH, String.valueOf(i) + ".zip"));}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}

55.zip切分成了10個小的zip包

2.Spring boot分片上傳接口

controller層

	@Resourceprivate FileUploadService fileUploadService;@RequestMapping(value = "/upload")public String upload(MultipartFileParam fileParam) throws IOException {try{return fileUploadService.fileUpload(fileParam);}catch (Exception e){e.printStackTrace();return "error";}}

service層

public interface FileUploadService {String fileUpload(MultipartFileParam fileParam) throws IOException;}

service實現層

@Slf4j
@Service
public class FileUploadServiceImpl implements FileUploadService {//合并后的文件的父目錄private String FILE_UPLOAD_DIR = "D:\\test\\fenpian";//分片文件大小private Integer CHUNK_SIZE = 10485760;/*** 分片上傳* @param fileParam* @return* @throws IOException*/private String chunkUpload(MultipartFileParam fileParam) throws IOException {// 是否為最后一片boolean lastFlag = false;int currentChunk = fileParam.getChunk();int totalChunk = fileParam.getTotalChunk();long totalSize = fileParam.getTotalSize();String fileName = fileParam.getName();String fileMd5 = fileParam.getMd5();MultipartFile multipartFile = fileParam.getFile();String parentDir = FILE_UPLOAD_DIR + File.separator + fileMd5 + File.separator;String tempFileName = fileName + "_tmp";// 寫入到臨時文件File tmpFile = tmpFile(parentDir, tempFileName, multipartFile, currentChunk, totalSize, fileMd5);// 檢測是否為最后一個分片(這里吧每個分片數據放到了一張表里,后續可以改為用redis記錄)FileChunkRecordExample example = new FileChunkRecordExample();example.createCriteria().andMd5EqualTo(fileMd5);long count = fileChunkRecordMapper.countByExample(example);if (count == totalChunk) {lastFlag = true;}if (lastFlag) {// 檢查md5是否一致log.info("是否最后一個分片:{}","是");if (!checkFileMd5(tmpFile, fileMd5)) {cleanUp(tmpFile, fileMd5);throw new RuntimeException("文件md5檢測不符合要求, 請檢查!");}System.out.println("開始重命名....");File newFile = renameFile(tmpFile, fileName);//解析文件數據 -解壓縮unzipSystem.out.println("開始解壓縮....");File zipFile = ZipUtil.unzip(newFile);//得到壓縮包內所有文件System.out.println("遍歷zipFile.....");File[] files = zipFile.listFiles();System.out.println("打印fileName.....");//解析文件,處理業務數據for (File file : files) {System.out.println(file.getName());}log.info("所有文件上傳完成, 時間是:{}, 文件名稱是:{}", DateUtil.now(), fileName);//所有數據都處理完成后,刪除文件和數據庫記錄cleanUp(new File(parentDir + fileName),fileMd5);}else{log.info("是否最后一個分片:{}","否");}return "success";}private File tmpFile(String parentDir, String tempFileName, MultipartFile file,int currentChunk, long totalSize, String fileMd5) throws IOException {log.info("開始上傳文件, 時間是:{}, 文件名稱是:{}", DateUtil.now(), tempFileName);long position = (currentChunk - 1) * CHUNK_SIZE;File tmpDir = new File(parentDir);File tmpFile = new File(parentDir, tempFileName);if (!tmpDir.exists()) {tmpDir.mkdirs();}RandomAccessFile tempRaf = new RandomAccessFile(tmpFile, "rw");if (tempRaf.length() == 0) {tempRaf.setLength(totalSize);}// 寫入該分片數據FileChannel fc = tempRaf.getChannel();MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE, position, file.getSize());map.put(file.getBytes());clean(map);fc.close();tempRaf.close();// 記錄已經完成的分片FileChunkRecord fileChunkRecord = new FileChunkRecord();fileChunkRecord.setMd5(fileMd5);fileChunkRecord.setUploadStatus(1);fileChunkRecord.setChunk(currentChunk);fileChunkRecordMapper.insert(fileChunkRecord);log.info("分片文件上傳完成, 時間是:{}, 文件名稱是:{}", DateUtil.now(), tempFileName);return tmpFile;}private void cleanUp(File file, String md5) {if (file.exists()) {file.delete();}// 刪除上傳記錄FileChunkRecordExample example = new FileChunkRecordExample();example.createCriteria().andMd5EqualTo(md5);fileChunkRecordMapper.deleteByExample(example);}/*** 最后一片接受完后執行* @param toBeRenamed* @param toFileNewName* @return*/private File renameFile(File toBeRenamed, String toFileNewName) {// 檢查要重命名的文件是否存在,是否是文件if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {log.info("File does not exist: " + toBeRenamed.getName());throw new RuntimeException("File does not exist");}String parentPath = toBeRenamed.getParent();File newFile = new File(parentPath + File.separatorChar + toFileNewName);// 如果存在, 先刪除if (newFile.exists()) {newFile.delete();}toBeRenamed.renameTo(newFile);return newFile;}private static void clean(MappedByteBuffer map) {try {Method getCleanerMethod = map.getClass().getMethod("cleaner");Cleaner.create(map, null);getCleanerMethod.setAccessible(true);Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(map);cleaner.clean();} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}}/*** 文件md5值檢查,最后一片文件合并后執行,* @param file    所有分片文件合并后的文件(正常情況下md5應該和前端傳過來的大文件的md5一致)* @param fileMd5 大文件的md5值* @return* @throws IOException*/private boolean checkFileMd5(File file, String fileMd5) throws IOException {FileInputStream fis = new FileInputStream(file);String checkMd5 = DigestUtils.md5DigestAsHex(fis).toUpperCase();fis.close();if (checkMd5.equals(fileMd5.toUpperCase())) {return true;}return false;}/*** 不分片* @param fileParam* @return*/private String singleUpload(MultipartFileParam fileParam) {MultipartFile file = fileParam.getFile();File baseFile = new File(FILE_UPLOAD_DIR);if (!baseFile.exists()) {baseFile.mkdirs();}try {file.transferTo(new File(baseFile, fileParam.getName()));Date now = new Date();FileRecord fileRecord = new FileRecord();String filePath = FILE_UPLOAD_DIR + File.separator + fileParam.getName();long size = FileUtil.size(new File(filePath));String sizeStr = size / (1024 * 1024) + "Mb";fileRecord.setFileName(fileParam.getName()).setFilePath(filePath).setUploadStatus(1).setFileMd5(fileParam.getMd5()).setCreateTime(now).setUpdateTime(now).setFileSize(sizeStr);//fileRecordMapper.insert(fileRecord);} catch (IOException e) {log.error("單獨上傳文件錯誤, 問題是:{}, 時間是:{}", e.getMessage(), DateUtil.now());}return "success";}}

后端入參實體類

package com.server.controller.bigFileUpload;import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.web.multipart.MultipartFile;/*** @description:* @date: created in 2021/10/6* @modified:*/
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class MultipartFileParam {/*** 是否分片*/private boolean chunkFlag;/*** 當前為第幾塊分片*/private int chunk;/*** 總分片數量*/private int totalChunk;/*** 文件總大小, 單位是byte*/private long totalSize;/*** 文件名*/private String name;/*** 文件*/private MultipartFile file;/*** md5值*/private String md5;}

合并后的文件放到了文件mdf的文件夾內
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38906.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38906.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38906.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue+java實現在線播放mp4視頻

java: 讀取本地視頻文件的流然后給response的輸出流 File file new File("/Users/zhangqingtian/Documents/水庫/Floodforecast/static/" videoName);BufferedInputStream inputStream new BufferedInputStream(new FileInputStream(file));response.setContentT…

ReactDOM模塊react-dom/client沒有默認導出報錯解決辦法

import ReactDOM 模塊“"E:/Dpandata/Shbank/rt-pro/node_modules/.pnpm/registry.npmmirror.comtypesreact-dom18.2.7/node_modules/types/react-dom/client"”沒有默認導出。 解決辦法 只需要在tsconfig.json里面添加配置 "esModuleInterop": true 即…

【C++】queue容器

1.queue容器基本概念 2.queue常用接口 #include <iostream> using namespace std;//隊列queue #include<queue>//創建Person類 class Person { public:Person(string name, int age){this->m_Name name;this->m_Age age;}string m_Name; //姓名int m_Age; …

mysql創建新用戶并授權

目錄 前言登錄到mysql創建用戶用戶授權更改用戶密碼參考 前言 略 登錄到mysql shell> mysql -h127.0.0.1 -P3306 -uroot -p******創建用戶 mysql> CREATE USER abc% IDENTIFIED BY 123456;用戶授權 mysql> GRANT all privileges ON ruoyi.* TO abc%;用戶ruoyi擁有…

優維低代碼實踐:自定義模板

優維低代碼技術專欄&#xff0c;是一個全新的、技術為主的專欄&#xff0c;由優維技術委員會成員執筆&#xff0c;基于優維7年低代碼技術研發及運維成果&#xff0c;主要介紹低代碼相關的技術原理及架構邏輯&#xff0c;目的是給廣大運維人提供一個技術交流與學習的平臺。 優維…

禾賽科技Q2營收交付雙新高,國產激光雷達從量變到質變

隨著2022年激光雷達元年、2023年城市智能輔助駕駛&#xff08;NOA&#xff09;元年相繼到來&#xff0c;激光雷達產業迎來爆發期。 今年以來&#xff0c;自動駕駛公司、汽車制造商以及移動出行公司等各路人馬積極推動城市級別的智能輔助駕駛全面落地&#xff0c;北京、上海、深…

通過css設置filter 屬性,使整個頁面呈現灰度效果,讓整個網頁變灰

通過css設置filter 屬性設置頁面整體置灰 效果圖: 通過設置 filter 屬性為 grayscale(100%)&#xff0c;頁面中的所有元素都會被應用灰色濾鏡效果&#xff0c;使整個頁面呈現灰度效果。 <style type"text/css"> html { filter: grayscale(100%); -webkit-f…

git pull 某一個文件或文件夾

QQ: 2967732156 背景&#xff1a; 在使用Oracle VM VirtualBox&#xff0c;進行Linux開發時&#xff0c;隨著使用內存越來越少&#xff0c;空間已不足拉取整個代碼庫。 Ubuntu1604內存夠&#xff0c;Ubuntu18.04內存不夠。 思路&#xff1a; 第一步&#xff1a;從問題本身…

TB/TM-商品詳情原數據(APP)

一、接口參數說明&#xff1a; item_get_app-獲得TB/TMapp商品詳情原數據&#xff0c;點擊更多API調試&#xff0c;請移步注冊API賬號點擊獲取測試key和secret 公共參數 請求地址: https://api-gw.onebound.cn/taobao/item_get_app 名稱類型必須描述keyString是調用key&…

考研 408 | 【計算機網絡】 應用層

導圖 網絡應用模型 客戶/服務器&#xff08;c/s&#xff09;模型 P2P模型 DNS 域名 域名服務器 域名解析過程 文件傳輸協議FTP FTP服務器和用戶端 FTP工作原理 電子郵件 電子郵件的信息格式 組成結構 郵件服務器的功能&#xff1a; 1.發送&接收郵件 2.給發件人報告郵…

使用windows Api簡單驗證ISO9660文件格式,以及裝載和卸載鏡像文件

使用IIsoImageManager接口簡單驗證ISO鏡像文件正確性,使用AttachVirtualDisk裝載ISO鏡像文件,和使用DetachVirtualDisk卸載,(只支持windows 8及以上系統) 導讀 IIsoImageManager 驗證ISO文件正確性AttachVirtualDisk 裝載鏡像文件DetachVirtualDisk 卸載鏡像文件其他相關函…

《游戲編程模式》學習筆記(四) 觀察者模式 Observer Pattern

定義 觀察者模式定義了對象間的一種一對多的依賴關系&#xff0c;當一個對象的狀態發生改變時&#xff0c;所有依賴于它的對象都得到通知并被自動更新。 這是定義&#xff0c;看不懂就看不懂吧&#xff0c;我接下來舉個例子慢慢說 為什么我們需要觀察者模式 我們看一個很簡…

PAT (Advanced Level) 甲級 1004 Counting Leaves

點此查看所有題目集 A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child. Input Specification: Each input file contains one test case. Each case starts with a line containing 0<N<100, …

如何在iPhone手機上修改手機定位和模擬導航?

如何在iPhone手機上修改手機定位和模擬導航&#xff1f; English Location Simulator&#xff08;定位模擬工具&#xff09; 是一款功能強大的 macOS 應用&#xff0c;專為 iPhone 用戶設計&#xff0c;旨在修改手機定位并提供逼真的模擬導航體驗。無論是為了保護隱私、測試位…

Angular 獨立組件入門

Angular 獨立組件入門 如果你正在學習 Angular&#xff0c;那么你可能已經聽說過獨立組件&#xff08;Component&#xff09;。顧名思義&#xff0c;獨立組件就是可以獨立使用和管理的組件&#xff0c;它們能夠被包含在其他組件中或被其他組件引用。 在本文中&#xff0c;我們…

【Unity腳本開源】記錄鼠標按下的位置和移動的距離來進行物體的旋轉,并在鼠標釋放后將物體恢復到初始旋轉位置

??作者&#xff1a;白日參商 &#x1f935;?♂?個人主頁&#xff1a;白日參商主頁 ??堅持分析平時學習到的項目以及學習到的軟件開發知識&#xff0c;和大家一起努力呀&#xff01;&#xff01;&#xff01; &#x1f388;&#x1f388;加油&#xff01; 加油&#xff01…

go-安裝部署

一、安裝go 詳細安裝方式可以查看官網 # 下載 wget https://golang.google.cn/dl/go1.21.0.linux-amd64.tar.gz # 解壓縮 tar -xzf go1.21.0.linux-amd64.tar.gz # 遷移目錄 mv go /usr/local # 配置環境變量 export PATH$PATH:/usr/local/go/bin # 檢查go的版本 go version有…

Python中的字符串與字符編碼

Hello&#xff0c;這里是Token_w的博客&#xff0c;歡迎您的到來 今天文章講解的是Python中的字符串與字符編碼&#xff0c;其中有基礎的理論知識講解&#xff0c;也有實戰中的應用講解&#xff0c;希望對你有所幫助 整理不易&#xff0c;如對你有所幫助&#xff0c;希望能得到…

PDM/PLM系統建設

僅供學習使用&#xff0c;會隨時更新 工程機械跨生命周期數據管理系統 來源&#xff1a;清華大學 淺論企業PDM/PLM系統建設成功經驗 來源&#xff1a;e-works 作者&#xff1a;陳凡 https://articles.e-works.net.cn/pdm/article149572.htm 隨著“中國制造2025”強基工程戰略的…

張俊林:由ChatGPT反思大語言模型(LLM)的技術精要

轉自&#xff1a;https://mp.weixin.qq.com/s/eMrv15yOO0oYQ-o-wiuSyw 導讀&#xff1a;ChatGPT出現后驚喜或驚醒了很多人。驚喜是因為沒想到大型語言模型&#xff08;LLM,Large Language Model&#xff09;效果能好成這樣&#xff1b;驚醒是頓悟到我們對LLM的認知及發展理念&a…