Java視頻斷點上傳的實現(xiàn)示例
什么是斷點續(xù)傳
通常視頻文件都比較大,所以對于媒資系統(tǒng)上傳文件的需求要滿足大文件的上傳要求。http協(xié)議本身對上傳文件大小沒有限制,但是客戶的網(wǎng)絡(luò)環(huán)境質(zhì)量、電腦硬件環(huán)境等參差不齊,如果一個大文件快上傳完了網(wǎng)斷了沒有上傳完成,需要客戶重新上傳,用戶體驗非常差,所以對于大文件上傳的要求最基本的是斷點續(xù)傳。
什么是斷點續(xù)傳:
引用百度百科:斷點續(xù)傳指的是在下載或上傳時,將下載或上傳任務(wù)(一個文件或一個壓縮包)人為的劃分為幾個部分,每一個部分采用一個線程進行上傳或下載,如果碰到網(wǎng)絡(luò)故障,可以從已經(jīng)上傳或下載的部分開始繼續(xù)上傳下載未完成的部分,而沒有必要從頭開始上傳下載,斷點續(xù)傳可以提高節(jié)省操作時間,提高用戶體驗性。
斷點續(xù)傳流程如下圖:

流程如下:
1、前端上傳前先把文件分成塊
2、一塊一塊的上傳,上傳中斷后重新上傳,已上傳的分塊則不用再上傳
3、各分塊上傳完成最后在服務(wù)端合并文件
文件分塊
文件分塊的流程如下:
1、獲取源文件長度
2、根據(jù)設(shè)定的分塊文件的大小計算出塊數(shù)
3、從源文件讀數(shù)據(jù)依次向每一個塊文件寫數(shù)據(jù)。
/**
* 文件分塊上傳測試
*/
@Test
public void testChunk(){
//獲取源文件
File sourceFile = new File("B:\\workspace\\test\\you.ncm");
//源文件字節(jié)大小
long length = sourceFile.length();
//分塊文件目錄
String chunkPath="B:\\workspace\\test\\chunk\\";
File chunkFolder = new File(chunkPath);
//檢查目錄是否存在
if (!chunkFolder.exists()) {
//不存在就創(chuàng)建
chunkFolder.mkdirs();
}
//分塊大小
long chunkSize = 1024*1024*1;
//分塊數(shù)量
long chunkNum = (long) Math.ceil(length * 1.0 / chunkSize);
//緩沖區(qū)大小
byte[] b = new byte[1024];
//使用RandomAccessFile訪問文件
try {
RandomAccessFile read = new RandomAccessFile(sourceFile, "r");
for (int i = 0; i < chunkNum; i++) {
//創(chuàng)建分塊文件
File file = new File(chunkPath + i);
//檢查文件是否存在,如果存在就刪除文件
if(file.exists()){
file.delete();
}
//創(chuàng)建一個新文件
boolean newFile = file.createNewFile();
if (newFile){
//向分塊文件中寫數(shù)據(jù)
RandomAccessFile write = new RandomAccessFile(file,"rw");
int len = -1;
while ((len = read.read(b)) != -1) {
write.write(b, 0, len);
//如果分塊文件的大小大于等于分塊大小就跳過本次循環(huán)
if (file.length() >= chunkSize) {
break;
}
}
write.close();
System.out.println("完成分塊"+i);
}
}
read.close();
} catch (Exception e) {
e.printStackTrace();
}
}
RandomAccessFile 是 Java 中的一個類,它允許對文件的任意位置進行讀寫操作。與其他的輸入/輸出流(如 InputStream 和 OutputStream)不同,RandomAccessFile 并不屬于它們的類系,而是直接繼承自 Object 類。它提供了類似于文件系統(tǒng)中的隨機訪問功能,因此得名“隨機訪問文件”。
以下是 RandomAccessFile 的一些主要特點和功能:
- 隨機訪問:
RandomAccessFile允許你直接跳到文件的任意位置來讀寫數(shù)據(jù)。這是通過使用seek(long pos)方法實現(xiàn)的,它可以將文件的指針移動到指定的位置。 - 讀寫功能:
RandomAccessFile既可以從文件中讀取數(shù)據(jù),也可以向文件中寫入數(shù)據(jù)。它提供了類似于InputStream的read()方法和類似于OutputStream的write()方法來執(zhí)行這些操作。 - 文件指針操作:除了
seek(long pos)方法外,RandomAccessFile還提供了getFilePointer()方法來返回文件記錄指針的當(dāng)前位置。 - 訪問模式:在創(chuàng)建
RandomAccessFile對象時,你需要指定一個訪問模式,它決定了文件是以只讀方式打開還是以讀寫方式打開。常見的訪問模式有 "r"(只讀)和 "rw"(讀寫)。 - 文件操作模式:在 JDK 1.6 及更高版本中,
RandomAccessFile還支持 "rws" 和 "rwd" 模式。在 "rws" 模式下,每次寫入操作都會確保數(shù)據(jù)被寫入到磁盤中;而在 "rwd" 模式下,只有在對文件執(zhí)行了某些特定的更新操作(如關(guān)閉文件或調(diào)用flush()方法)后,數(shù)據(jù)才會被寫入到磁盤中。 - 內(nèi)存映射文件:雖然
RandomAccessFile提供了強大的文件訪問功能,但在某些情況下,使用 JDK 1.4 引入的“內(nèi)存映射文件”可能會更高效。內(nèi)存映射文件允許你將文件的一部分或全部映射到內(nèi)存中,從而可以像訪問內(nèi)存一樣快速地訪問文件。
文件合并
文件合并流程:
1、找到要合并的文件并按文件合并的先后進行排序。
2、創(chuàng)建合并文件
3、依次從合并的文件中讀取數(shù)據(jù)向合并文件寫入數(shù)
文件合并的測試代碼 :
//測試文件合并方法
@Test
public void testMerge(){
try {
//獲取源文件
File sourceFile = new File("B:\\workspace\\test\\you.ncm");
//分塊文件目錄
String chunkPath="B:\\workspace\\test\\chunk\\";
//合并后的文件
File mergeFile = new File("B:\\workspace\\test\\you1.ncm");
if (mergeFile.exists()) {
mergeFile.delete();
}
//創(chuàng)建新的合并文件
mergeFile.createNewFile();
RandomAccessFile write = new RandomAccessFile(mergeFile,"rw");
//指針指向文件頂端
write.seek(0);
//緩沖區(qū)
byte[] b = new byte[1024];
//獲取分塊文件數(shù)組
File file = new File(chunkPath);
File[] files = file.listFiles();
// 轉(zhuǎn)成集合,便于排序
List<File> fileList = Arrays.asList(files);
//使用工具類和自定義比較類進行排序
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
Integer o1Name = Integer.parseInt(o1.getName());
Integer o2Name=Integer.parseInt(o2.getName());
return o1Name-o2Name;
}
});
//合并文件
for (File file1 : fileList) {
RandomAccessFile read = new RandomAccessFile(file1,"r");
int len = -1;
while ((len = read.read(b)) != -1) {
write.write(b, 0, len);
}
read.close();
}
write.close();
//校驗文件
FileInputStream fileInputStream = new FileInputStream(sourceFile);
FileInputStream mergeFileStream = new FileInputStream(mergeFile);
//取出原始文件的md5
String originalMd5 = DigestUtils.md5Hex(fileInputStream);
//取出合并文件的md5進行比較
String mergeFileMd5 = DigestUtils.md5Hex(mergeFileStream);
if (originalMd5.equals(mergeFileMd5)) {
System.out.println("合并文件成功");
} else {
System.out.println("合并文件失敗");
}
} catch (Exception e) {
e.printStackTrace();
}
}視頻上傳流程

1、前端對文件進行分塊。
2、前端上傳分塊文件前請求媒資服務(wù)檢查文件是否存在,如果已經(jīng)存在則不再上傳。
3、如果分塊文件不存在則前端開始上傳
4、前端請求媒資服務(wù)上傳分塊。
5、媒資服務(wù)將分塊上傳至MinIO。
6、前端將分塊上傳完畢請求媒資服務(wù)合并分塊。
7、媒資服務(wù)判斷分塊上傳完成則請求MinIO合并文件。
8、合并完成校驗合并后的文件是否完整,如果不完整則刪除文件。
測試將分塊文件上傳至minio
//將分塊文件上傳至minio
@Test
public void uploadChunk(){
String chunkFolderPath = "B:\\workspace\\test\\chunk\\";
File chunkFolder = new File(chunkFolderPath);
//分塊文件
File[] files = chunkFolder.listFiles();
//將分塊文件上傳至minio
for (int i = 0; i < files.length; i++) {
try {
UploadObjectArgs uploadObjectArgs = UploadObjectArgs
.builder()
.bucket("testbucket")//桶名
.object("chunk/" + i)//存儲路徑+文件名
.filename(files[i].getAbsolutePath())
.build();
minioClient.uploadObject(uploadObjectArgs);
System.out.println("上傳分塊成功"+i);
} catch (Exception e) {
e.printStackTrace();
}
}
}測試通過minio的合并文件
//合并文件,要求分塊文件最小5M
@Test
public void test_merge() throws Exception {
List<ComposeSource> sources = new ArrayList<>();
for (int i = 0; i <=7; i++) {
ComposeSource composeSource = ComposeSource
.builder()//指定分塊文件信息
.bucket("testbucket")
.object("chunk/" + (Integer.toString(i)))//目標(biāo)文件信息
.build();
sources.add(composeSource);
}
ComposeObjectArgs composeObjectArgs = ComposeObjectArgs
.builder()
.bucket("testbucket")
.object("merge01.npm")//目標(biāo)文件
.sources(sources)//源文件
.build();
//合并文件
minioClient.composeObject(composeObjectArgs);
}測試minio清除分塊文件
//清除分塊文件
@Test
public void test_removeObjects(){
//合并分塊完成將分塊文件清除
List<DeleteObject> deleteObjects = Stream.iterate(0, i -> ++i)
.limit(2)//循環(huán)幾次
.map(i -> new DeleteObject("chunk/".concat(Integer.toString(i))))
.collect(Collectors.toList());
RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket("testbucket").objects(deleteObjects).build();
Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
results.forEach(r->{
DeleteError deleteError = null;
try {
deleteError = r.get();
} catch (Exception e) {
e.printStackTrace();
}
});
}到此這篇關(guān)于Java視頻斷點上傳的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)Java視頻斷點上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot 項目使用jasypt加密數(shù)據(jù)源的方法
Jasypt 是一個 Java 庫,它允許開發(fā)者以最小的努力為他/她的項目添加基本的加密功能,而且不需要對密碼學(xué)的工作原理有深刻的了解。接下來通過本文給大家介紹springboot 項目使用jasypt加密數(shù)據(jù)源的問題,一起看看吧2021-11-11
使用maven項目pom.xml文件配置打包功能和靜態(tài)資源文件自帶版本號功能
在Maven項目中,通過pom.xml文件配置打包功能,可以控制構(gòu)建過程,生成可部署的包,同時,為了緩存控制與版本更新,可以在打包時給靜態(tài)資源文件如JS、CSS添加版本號,這通常通過插件如maven-resources-plugin實現(xiàn)2024-09-09

