总记
在开发过程中,难免会碰到文件上文的问题,遇到文件上传势必会碰到文件体积过大的问题。通常我们的解决办法就是对大文件依据某个单位进行切割,并存储;当然,有些博客在切分完成之后选择生成子文件的方式进行存储;也可以选择将切割完的内容以blob二进制的方式存储在数据库的某一个字段中。本文依照,存储进数据库的方式进行开发。
文件切割
@Override
public R<List<FileResultDTO>> getStatusByFileIds(String files) {
R<List<FileResultDTO>> r = new R<List<FileResultDTO>>();
if(StringUtils.isBlank(files)) {
r.setMsg("没有文件ID");
r.setObj(null);
r.setState("error");
return r;
}
List<FileResultDTO> result = new ArrayList<>();
List<FileStatusDTO> dtoList = JSONObject.parseArray(files, FileStatusDTO.class);
if(null != dtoList) {
if(dtoList.size()>0) {
dtoList.stream().forEach(el ->{
if(null != el.getFileIds()) {
if(el.getFileIds().size()>0) {
List<String> fileIds = el.getFileIds().stream().distinct().collect(Collectors.toList());
List<FileRecord> recordList = contentMapper.getListByFileIds(fileIds);
List<Integer> idList = recordList.stream().map(FileRecord::getStatus).distinct()
.collect(Collectors.toList());
if(1 == idList.size()) {
FileResultDTO dto = new FileResultDTO();
dto.setId(el.getId());
if(-1 == idList.get(0)) {
dto.setStatus(0);
}else if(0 == idList.get(0)) {
dto.setStatus(1);
}else if(1 == idList.get(0)) {
dto.setStatus(2);
}
result.add(dto);
}else if(idList.size()>0){
FileResultDTO dto = new FileResultDTO();
dto.setId(el.getId());
dto.setStatus(1);
result.add(dto);
}
}
}
});
}
}
r.setMsg("");
r.setObj(result);
r.setState("success");
return r;
}
@Override
public void synchroInnerByFileId(String id) {
//查询记录表获取文件记录信息以及文件id
FileRecord record = recordMapper.selectByPrimaryKey(id);
FileBasics fileBasicCondition = new FileBasics();
fileBasicCondition.setFileVersionId(record.getFileId());
FileBasics el = fileMapper.selectOne(fileBasicCondition);
try {
FilePathDTO dto = new FilePathDTO();
BeanUtils.copyProperties(el, dto);
dto.setOutPath(el.getOutPath());
if("dwg".equals(el.getExt().toLowerCase())|| "dxf".equals(el.getExt().toLowerCase())) {
readfile(el.getFilePath(), dto, id,1,0);
}else {
if("0".equals(el.getStatus())) {
readfile(el.getFilePath(), dto, id,1,0);
}else if("2".equals(el.getStatus())) {
if(StringUtils.isNotBlank(el.getOutPath())) {
if("rvt".equals(el.getExt().toLowerCase()) || "ifc".equals(el.getExt().toLowerCase()) || "rfa".equals(el.getExt().toLowerCase())){
String[] json = el.getOutPath().split("beTile.json");
ZipUtil.zip(json[0],json[0] + "tile-"+el.getFileVersionId()+".zip");
// record.setFileHash(FileHash.md5HashCode(json[0] + "tile-"+el.getFileVersionId()+".zip"));
readfile(json[0] + "tile-"+el.getFileVersionId()+".zip", dto, id,0,1);
}else {
readfile(el.getOutPath(), dto, id,0,0);
}
}
fileLevel = 0;
readfile(el.getFilePath(), dto, id,1,0);
}
}
record.setStatus(0);
recordMapper.updateByPrimaryKeySelective(record);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
outer.remove(id);
}
}
@Override
public void addSynchroTask(List<String> fileId) {
List<FileBasics> outerList = fileMapper.selectOuterFilesByIds(fileId);
// if(null != outerList) {
System.out.println("查询数据:"+JSONObject.toJSONString(outerList.get(0)));
if (outerList.size() > 0) {
outerList.stream().forEach(el -> {
Example example = new Example(FileRecord.class);
example.createCriteria().andEqualTo("fileId", el.getFileVersionId());
int result = recordMapper.selectCountByExample(example);
String id = null;
if (result == 0) {
fileExCount = 0;
fileCount = 0;
fileLevel = 0;
FileRecord record = new FileRecord();
id = UUIDUtil.getPK();
record.setId(id);
record.setStatus(-1);
record.setFileStatus(0);
record.setCreateDate(new Date());
record.setFileId(el.getFileVersionId());
record.setEnvirement(inner.getIdx());
if("dwg".equals(el.getExt().toLowerCase())|| "dxf".equals(el.getExt().toLowerCase())) {
try {
// record.setFileHash(FileHash.md5HashCode(el.getFilePath()));
readfileCount(el.getFilePath(), 1);
} catch (Exception e) {
e.printStackTrace();
}
record.setFileType(1);
}else {
if("0".equals(el.getStatus())) {
try {
// record.setFileHash(FileHash.md5HashCode(el.getFilePath()));
readfileCount(el.getFilePath(), 1);
} catch (Exception e) {
e.printStackTrace();
}
record.setFileType(1);
}else if("2".equals(el.getStatus())) {
if(StringUtils.isNotBlank(el.getOutPath())) {
try {
// record.setFileHash(FileHash.md5HashCode(el.getOutPath()));
readfileCount(el.getFilePath(), 0);
} catch (Exception e) {
e.printStackTrace();
}
record.setFileType(0);
}else {
try {
// record.setFileHash(FileHash.md5HashCode(el.getFilePath()));
readfileCount(el.getFilePath(), 1);
} catch (Exception e) {
e.printStackTrace();
}
record.setFileType(1);
}
}
}
record.setIndexExNum(fileExCount);
record.setIndexNum(fileCount);
recordMapper.insert(record);
System.out.println("插入成功!");
}
});
}
// }
}
@Override
public void synchroTask() {
int size = 5 - outer.size();
if(size == 0)
return;
List<FileRecord> fileRecords = contentMapper.getContentPage(outer.stream().collect(Collectors.toList()), 5);
for (FileRecord fileRecord : fileRecords) {
if(outer.contains(fileRecord.getId()))
continue;
outer.add(fileRecord.getId());
executorService.execute(() -> {
synchroInnerByFileId(fileRecord.getId());
outer.remove(fileRecord.getId());
});
}
}
接下来我将对以上代码进行讲解,此文章只是将我当时开发的思路讲出来,至于实现,每个人的需求都是不同的,可以根据需求做更改。
首先,正常的逻辑应该是,我们首先采用io流的方式,对文件的路径进行读取,获取到了byte[],然后按照单位进行切割,此处我是按照60M对文件进行切割,切割完毕存储到数据库中,为以后做合并文件之用。
我的程序开发思路是这样的,首先我这个程序目的是为了定时切割文件,但是又不能同时无限切割,因为这样会严重占用系统性能,所以设置一个线程集合,大小为5,每次最多读取5个文件,每次读取文件按照60M大小进行切割,如果小于等于60M,则保留源文件blob,进行存储。
重点注意,不要忘记,如果大于60M切割存储的时候不要忘记记录切割内容的顺序,方便合并,否则即使合并文件完成也无法打开。
public List<FileContent> readfile(String filepath, FilePathDTO basics, String id,Integer fileType,Integer isZip)
throws FileNotFoundException, IOException {
int eachSize = 60*1024*1024;
List<FileContent> contentList = new ArrayList<>();
try {
File file = new File(filepath);
if (!file.isDirectory()) {
byte[] data = new byte[(int) file.length()];// 新建字节数组
try {
FileInputStream fis = new FileInputStream(file);
fis.read(data);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
if (data.length > 0) {
int fileNumber;// 计算被划分成多少份子文件
if (file.length() % eachSize == 0) {
fileNumber = (int) (data.length / eachSize);
} else {
fileNumber = (int) (data.length / eachSize) + 1;
}
if (fileNumber == 0) {
//此处是我开发需求,可根据实际更改
FileContent content = new FileContent();
content.setId(UUIDUtil.getPK());
content.setFileContent(data);
content.setFileIndex(-1);
content.setFileLevel(fileLevel);
content.setFileId(basics.getFileVersionId());
content.setFileType(fileType);
content.setRecordId(id);
content.setStatus("0");
if(1 == isZip) {
content.setZipPosition(filepath);
String[] json = basics.getOutPath().split("beTile.json");
content.setFilePosition(json[0]);
}else {
content.setFilePosition(file.getAbsolutePath());
}
content.setEnviroment(inner.getIdx());
content.setIsZip(isZip);
contentMapper.insertSelective(content);
} else {
for (int i = 0; i < fileNumber; i++) {
byte[] eachContent = null;
if (i != (fileNumber - 1)) {
// 如果不是最后一个,每个长度相同
eachContent = Arrays.copyOfRange(data, eachSize * i, eachSize * (i + 1));
} else {
// 最后一个(长度不一致)
eachContent = Arrays.copyOfRange(data, eachSize * i, data.length);
}
FileContent content = new FileContent();
content.setId(UUIDUtil.getPK());
content.setFileContent(eachContent);
content.setFileIndex(-1);
content.setPointIndex(i);
content.setFileLevel(fileLevel);
content.setFileId(basics.getFileVersionId());
content.setFileType(fileType);
content.setRecordId(id);
content.setIsZip(isZip);
content.setStatus("0");
if(1 == isZip) {
content.setZipPosition(filepath);
String[] json = basics.getOutPath().split("beTile.json");
content.setFilePosition(json[0]);
}else {
content.setFilePosition(file.getAbsolutePath());
}
content.setEnviroment(inner.getIdx());
contentMapper.insertSelective(content);
}
}
}
} else if (file.isDirectory()) {
File[] fileList2 = file.listFiles();
List<File> fileList = new ArrayList<>();
for (int i = 0; i < fileList2.length; i++) {
if(!fileList2[i].isDirectory()) {
fileList.add(fileList2[i]);
}
}
for (int i = 0; i < fileList2.length; i++) {
if(fileList2[i].isDirectory()) {
fileList.add(fileList2[i]);
}
}
fileLevel ++;
for (int i = 0; i < fileList.size(); i++) {
if (!fileList.get(i).isDirectory()) {
byte[] data = new byte[(int) fileList.get(i).length()];// 新建字节数组
try {
FileInputStream fis = new FileInputStream(fileList.get(i));
fis.read(data);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
if (data.length > 0) {
int fileNumber;// 计算被划分成多少份子文件
if (file.length() % eachSize == 0) {
fileNumber = (int) (data.length / eachSize);
} else {
fileNumber = (int) (data.length / eachSize) + 1;
}
if (fileNumber == 0) {
FileContent content = new FileContent();
content.setId(UUIDUtil.getPK());
content.setFileContent(data);
content.setFileIndex(i);
content.setFileId(basics.getFileVersionId());
content.setFileLevel(fileLevel);
content.setFileType(fileType);
content.setRecordId(id);
content.setIsZip(isZip);
content.setStatus("0");
if(1 == isZip) {
content.setZipPosition(filepath);
String[] json = basics.getOutPath().split("beTile.json");
content.setFilePosition(json[0]);
}else {
content.setFilePosition(file.getAbsolutePath());
}
content.setEnviroment(inner.getIdx());
contentMapper.insertSelective(content);
} else {
for (int j = 0; j < fileNumber; j++) {
byte[] eachContent = null;
if (i != (fileNumber - 1)) {
// 如果不是最后一个,每个长度相同
eachContent = Arrays.copyOfRange(data, eachSize * i, eachSize * (i + 1));
} else {
// 最后一个(长度不一致)
eachContent = Arrays.copyOfRange(data, eachSize * i, data.length);
}
FileContent content = new FileContent();
content.setId(UUIDUtil.getPK());
content.setFileContent(eachContent);
content.setFileIndex(i);
content.setPointIndex(j);
content.setFileLevel(fileLevel);
content.setFileId(basics.getFileVersionId());
content.setFileType(fileType);
content.setRecordId(id);
content.setIsZip(isZip);
content.setStatus("0");
if(1 == isZip) {
content.setZipPosition(filepath);
String[] json = basics.getOutPath().split("beTile.json");
content.setFilePosition(json[0]);
}else {
content.setFilePosition(file.getAbsolutePath());
}
content.setEnviroment(inner.getIdx());
contentMapper.insertSelective(content);
}
}
}
} else if (fileList.get(i).isDirectory()) {
fileLevel ++;
readfile(fileList.get(i).getAbsolutePath(), basics, id,fileType,isZip);
}
}
}
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
}
return contentList;
}
此处代码,为读取文件并切割的方法,之所以用到了迭代,是因为我这个接收的地址参数可能会出现是文件夹,而不是文件,所以就需要迭代读取地址,把里面的文件都读取出来。
总结
由于篇幅问题,合并我就不在这里说明,我将在我下一篇文章进行说明,当然,这个只是我粗略记录一下心得,以后会不断更新完善这篇文章。