师兄给的说明↓
现在想象一个场景,你要将文件夹上传到网盘备份。
但是,假如现在有个大文件几G,可能上传到99%网络断了,重来(下载也是)。还有一种情况,可能某个文件夹下有大量的小文件(几十万的量级) ,可能上传的过程中,网络都跑不满,全部卡在cpu解析和读取上了。
所以现在需要把文件大小居中,弄成适当的大小(比如10MB一个文件),现在的任务就是将一个文件夹下的文件打包成10MB的文件块。
- 问题一:
比如有如下文件的大小: 3个12M,10个127k,100个7k的文件
预期分配结果是什么?是最优的吗?
思路和需要用到的知识
- 遍历文件夹信息,就是获取每个文件的大小,路径什么的
- 编写算法,输入所有文件的信息,进行打包算法,
我当时想的是:把12m的都拆成10m+2m,再把3个2m部分+剩下的……
-
答案:
一共 36M+1270k+700k =37M+746K
方案一: 10 + (2+8)+(4+6) +(6+剩下的)
方案二: 12 +12 +12 +剩下的
实际情况得从目录结构分析,可能会有不同的结果,也就是可能100个7k里面有几个和一个12M是同一子目录下的,应当打包到一起吧?不然你还原该文件夹的时候要处理两个数据块
考虑到实际,虽然说打包成10M 但是一个10M+1B的文件总不能打包成两个吧,所以应该是约10M 实际大小大概是 7M~14M波动(这样超过14M即可拆分成两个7M+的文件了,当然还有6~12 8~16 但是离平均的10M较远)
-
问题二:
输出是怎么样的?
- 拿上一步的输出,执行打包作业
- 问题三: 重点
找个大一点的文件夹,观察执行中cpu,磁盘的调度和使用情况,性能最优了吗?
体会什么是阻塞。
- 问题四:
遍历目录树用来什么遍历方法,别的遍历顺序也可以吗?有什么区别,哪一种最好?
- 问题五:
考虑过算法的逆向吗?即从打包后的文件还原之前的目录结构
- 问题六:
考虑过用户场景吗?
比如一个场景:
将一个图片文件夹或者视频文件夹打包后,将数据包上传到百度网盘。现在需要拿到里面某个文件,需要下载多少数据包?
a)是所有数据包?还是先下载打包信息,然后找到含有这个所需文件的数据包再下载需要的就行?
b)你的打包算法有对这种情况进行调优吗?回头看看问题四
- 问题七:
你的代码怎么保证是正确的?
我思路就是先遍历文件夹把大于10m就拆成最大为10的子文件 然后再遍历文件夹把<10m的文件组装为最大10m的文件块 就是只能实现要求的内容 但是不优……有一些想法但是不知道要怎么实现,还是不熟悉JAVA语法,不知道怎么实现&不知道能不能实现,TAT。
目前代码:
package FilePacker;
import java.io.*;
import java.util.Arrays;
import javax.security.auth.DestroyFailedException;
public class FilePacker {
private static void splitFile(File f, int eachSize) {
if(f.length()>10 * 1024 * 1024) {//该文件大于10m
int count = (int)(Math.ceil(f.length() / eachSize))+1;//块数
try {
/**创建输入输出流对象*/
InputStream inf = new FileInputStream(f);
OutputStream[] outf = new FileOutputStream[count];
/**创建文件夹,存储各小块文件*/
int no = f.getName().lastIndexOf(".");
String str = f.getParent()+"\\"+f.getName().substring(0, no );/**目录路径*/
File dirfile = new File(str);
if(!dirfile.exists()) {
dirfile.mkdirs();
}
/**创建各小块文件并命名*/
File[] dir_f = new File[count];
/**获取文件类型*/
String fName = f.getName();
String fPattern = fName.substring(fName.lastIndexOf("."), fName.length());
for(int j=0; j<count; j++) {
String newPath = str+"\\"+f.getName().substring(0, no)+"-"+j+fPattern;
dir_f[j] = new File(newPath);
outf[j] = new FileOutputStream(dir_f[j]);
}
/**写入各块内容*/
int s,m=0, n=10*1024;
byte[] buffer = new byte[n];
s = inf.read(buffer, 0, n);
while(s != -1&& m<count) {
if(dir_f[m].length() < 10*1024) {
outf[m].write(buffer, 0, n);
s = inf.read(buffer, 0, n);
}
if(dir_f[m].length() == 10*1024){
outf[m].close();
m = m+1;
int off = (int)(f.length()-m*10*1024);
if(off<10*1024) {
outf[m].write(buffer, 0, off);
outf[m].close();
break;
}
}
}
inf.close();
f.delete();
}
catch(IOException ioe) {
System.out.println("IO 异常");
}
catch(IndexOutOfBoundsException ine) {
System.out.println("数组越界 异常");
}
catch(Exception e) {
System.out.println("异常");
}
}
}
public static void divide( String fPath ) {
File f = new File(fPath);
/**文件存在*/
if(f.exists()){
/**是单个文件*/
if(f.isFile() && f.length() > 10*1024*1024) {
/**调用单文件分割方法*/
splitFile(f, 10*1024*1024);
}
/**是目录*/
else if(f.isDirectory() && f.length() > 10*1024*1024) {
/**目录文件数组化*/
File[] dir = f.listFiles();
for(int i=0; i<dir.length; i++) {
if(dir[i].exists() && dir[i].length() > 10*1024*1024){
if(dir[i].isFile()) {
splitFile(dir[i], 10*1024*1024);
}
else if(dir[i].isDirectory() && dir[i].length() > 10*1024*1024) {
divide(dir[i].getAbsolutePath());
}
}
}
}
}
else {
System.out.println(fPath + " 不存在文件!");
}
}
private static void murgeFile(File dir, File dst) throws IOException {
String[] children = dir.list();
FilenameFilter filter = new FilenameFilter(){
public boolean accept(File dir, String name) {
return true;
}
};
File filePath = new File("c:\\Users\\73426\\Desktop\\PPT\\test\\作业要求");//需要拼装的目录
String fileIndex="0";
File FileBig = new File(filePath + "/" + "Files"+"_"+fileIndex);
BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream(FileBig));
children = dir.list(filter);
for (int i = 0; i < children.length; i++) {
String inhalt = children[i];
FileInputStream in = new FileInputStream(dir + "/" + inhalt);
byte[] buffer = new byte[1024];
int len=0;
if (FileBig.length() /1024 / 1024 < 10) {
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.flush();// out.close();
} else {
int fileIndexInt = Integer.parseInt(fileIndex) + 1;
FileBig = new File(filePath + "/" + "Files" + "_"
+ fileIndexInt);
out = new BufferedOutputStream(
new FileOutputStream(FileBig));
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.flush();
//out.close();
fileIndex = String.valueOf(fileIndexInt);
}
}
out.close();
}
public static void start()
{
String path = "c:\\Users\\73426\\Desktop\\PPT\\test";//要切割的文件/目录路径
divide(path);//切割
File dir = new File("c:\\Users\\73426\\Desktop\\PPT\\test\\作业要求");
File out=new File("c:\\Users\\73426\\Desktop\\PPT\\test\\作业要求");
try {
murgeFile(dir,out);
//由于是在网上找的方法,东拼西凑改的,合并这个地方还不够灵活,应该要再创建一个方法遍历原目录所有文件进行合并处理,遇到子文件夹再将子文件也拼接(待完成)
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args)throws IOException {
start();
}
}