文件拆分(切割)
将大文件拆分成多个小文件,每个小文件的大小可以自定义,这样可以方便传输,也可以方便存储。 步骤:
-
创建一个文件输入流对象,关联数据源文件
-
创建一个文件输出流对象,关联子文件
-
为了防止子文件覆盖,需要在文件名后面加上编号
-
会有多个子文件,所以需要使用循环
-
-
读取数据源文件,将读取到的字节写入到子文件
-
关闭资源
文件合并
将多个小文件合并成一个大文件,这样可以方便存储,也可以方便传输。 步骤:
-
创建一个文件输出流对象,关联目的地文件
-
创建一个文件输入流对象,关联数据源文件
-
会有多个子文件,所以需要使用循环
-
-
读取数据源文件,将读取到的字节写入到目的地文件
-
关闭资源
代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test01 {
public static void main(String[] args) {
File file = new File("d:/temp/jianghu.mp4"); //原文件
long size = 1024 * 1024; //指定拆分后每个文件的大小
File dir = new File("d:/temp/day01", "cutter"); //拆分后文件地址
int count = split(file, size, dir); //计算拆分后文件数量
System.out.println(count);
}
public static int split(final File original, final long size, final File directory) {
// 参数校验
if (original == null || !original.isFile()) {
return 0;
}
// 输出文件夹是否存在
if (!directory.exists()) {
directory.mkdirs();
}
int count = 0; // 拆分文件的数量
try (FileInputStream fis = new FileInputStream(original)) {
// 单文件大小一个数组能读取到范围
if (size <= Integer.MAX_VALUE - 8) {
//最大数组大小定义为Integer.MAX_VALUE - 8,作为自己需要8 bytes存储大小
byte[] bytes = new byte[(int) size];
int readSize; // readSize 读取的字节数
while (fis.available() != 0) {//可读取字节数
readSize = fis.read(bytes);
File outFile = new File(directory, count++ + ".kfm");
// 用输出流创建子文件
FileOutputStream fos = new FileOutputStream(outFile);
fos.write(bytes, 0, readSize);//这么做防止写入重复的字节
System.out.println("分隔文件:" + outFile);
fos.close();
}
} else {
// 一次读取不能完整读取一个文件 size > Integer.MAX_VALUE
// 当前被分隔的文件是否以分隔完毕
while (fis.available() != 0) { // 每循环一个会创建一个子文件
// 用输出流创建子文件
File outFile = new File(directory, count++ + ".part");
FileOutputStream fos = new FileOutputStream(outFile);
long sum = size; // 单文件大小
while (sum > Integer.MAX_VALUE - 8 && fis.available() != 0) {
byte[] bytes = new byte[Integer.MAX_VALUE - 8];
int readSize = fis.read(bytes);
fos.write(bytes, 0, readSize);
sum -= readSize;
}
// 没有满足单文件大小, 还有内容没读取
if (fis.available() != 0) {
byte[] bytes = new byte[(int) sum];
int readSize = fis.read(bytes);
fos.write(bytes, 0, readSize);
}
fos.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
}
package com.ketan.lianxi26;
import java.io.*;
import java.util.Arrays;
public class Test02 {
public static void main(String[] args) {
//拆分后文件地址
File dir = new File("d:/temp/day01/cutter");
//合并后文件存放地址
File file = new File("d:/temp/合并后的视频.mp4");
long size = join(dir, file);
System.out.println(size);
// String path = "d:/io/cutter/10.kfm";
// String path1 = "d:/io/cutter/2.kfm";
//
// System.out.println(path.compareTo(path1));
}
/**
* 将指定目录中的多个文件片段依次序合并到同一个文件中,并返回合并后的文件体积
*
* @param dir 存放文件片段的目录(即拆分文件后的N个小文件的存放目录)
* @param target 表示合并后的文件的存放路径
* @return 返回合并后的文件体积
* <p>
* 合并后文件被损坏,原因:合并时子文件的顺序不对
* 解决方案:
* 1. 拆分时文件名按照顺序命名(单线程下可用时间戳做文件名)
* 2. 根据文件名自定义排序规则
*/
public static long join(final File dir, final File target) {
long size = 0; // 声明用于统计合并后的文件体积的变量
// 参数校验
if (dir == null || !dir.isDirectory()) {
return size;
}
// 参数校验
if (target == null) {
return size;
}
if (!target.getParentFile().exists()) {
target.getParentFile().mkdirs();
}
// 合并文件
File[] files = dir.listFiles((file, name) -> name.endsWith(".kfm"));
if (files != null && files.length > 0) { // 判断是否有文件片段
// 保证顺序
Arrays.sort(files, (f1, f2) -> {
String name1 = f1.getName();
String name2 = f2.getName();
int index1 = Integer.parseInt(name1.split("\\.")[0]);
int index2 = Integer.parseInt(name2.split("\\.")[0]);
return index1 - index2;
});
try {
FileOutputStream fos = new FileOutputStream(target); // 创建文件输出流
for (File file : files) { // 遍历文件片段
if (file.length() <= Integer.MAX_VALUE - 8) { // 一次可以完全读取
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = fileInputStream.readAllBytes();
fos.write(bytes);
fileInputStream.close();
} else {
// 一次不能完全读取
FileInputStream fileInputStream = new FileInputStream(file);
while (fileInputStream.available() != 0) {
byte[] bytes = new byte[Integer.MAX_VALUE - 8];
int readSize = fileInputStream.read(bytes);
fos.write(bytes, 0, readSize);
}
fileInputStream.close();
}
size += file.length();
}
fos.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return size; // 返回合并后的文件体积
}
}