需求:
zip包中有个json文件,需要将json文件进行转化再生成一个json文件并放到同级目录下。
实现过程
1、未进行解压zip文件,通过zipProp.entries()读取zip包中的文件元素,读取到json文件就进行转化再次生成一个json文件,然后将生成的json文件直接append到原zip文件中(存在于zip包的根目录下),这样的操作不需要对zip包进行解压再压缩,处理速度快,但是生成的json文件和之前的json文件不在同一个目录下。
2、若需要将json文件放置到固定的文件夹中,就必须进行解压,因为.zip文件是一个可执行文件并不属于一个文件夹,无法操作zip包内部属性。
3、zip包进行解压,将生成的json文件copy放置固定目录后再压缩。执行
出现的问题
执行导致服务重启
这了这么长时间的代码,除了内存溢出导致崩溃,第一次出现执行到某行代码直接崩溃,内存,cpu都正常。
附图:
排查过程
- 解压文件正确
- json文件copy正确
- 压缩包正确
- 以上都符合预期,说明业务流程跑完,进入下一次循环
- 由于上传解压压缩都使用同一文件名,再次生成的zip包会将原文件覆盖,entries.nextElement()下一项元素调用native方法,涉及底层native方法进入本地C库。
- 猜想:由于原文件被替换,原文件找不到,file链接引用的文件不是原文件,或者file链接已断开
疑问:
文件被替换,找原文件下一项元素,利用操作系统的IO操作,调用了C库,为什么会使服务进程重启呢。
附实现过程
public Map<String, Object> uploadFile(CommonsMultipartFile mFile, HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
int state = 1000;
try {
if(mFile == null){
log.warn("ClipUploadServiceImpl mFile 空");
return null;
}
if(mFile.getSize() == 0){
log.warn("ClipUploadServiceImpl mFile 文件内容空,请重新选择文件");
return null;
}
String prefixStr = "file.51vv.com";
String suffix = ""; //文件后缀
String route = "/fileUpload/";
String saveDir = "/upload" + route; //定义文件存放地址
String filename = mFile.getOriginalFilename(); //获得原始文件名
// String fileNameMD5 = MD5Util.getEncoderByMd5(filename);
// log.info("FileName的值:[{}],文件名的md5值:[{}]", filename, fileNameMD5);
log.info("FileName的值:[{}]", filename);
long fileSize = mFile.getSize();
if (fileSize > 20000 * 1024) {
log.warn("[{}]文件太大",filename);
map.put("msg", filename+"上传文件尺寸超过了限定大小");
map.put("state", 2000);
return map;
}
if (filename.indexOf(".") != -1) {
suffix = filename.substring(filename.lastIndexOf("."));
}
suffix = suffix.toLowerCase();
String saveFilePath = saveDir + filename;
File dir = new File(request.getSession().getServletContext().getRealPath(saveDir)); //文件夹
// 判断文件夹不存在就创建
if (!dir.exists()) {
dir.mkdirs();
log.info("创建目录:[{}]", dir.getName());
}
File file = new File(request.getSession().getServletContext().getRealPath(saveFilePath));
mFile.getFileItem().write(file);
String fileMD5 = MD5FileUtility.getFileMD5String(file);
String saveFile = route + fileMD5 + suffix; //远程服务器相对url
String url = prefixStr + saveFile;
if (".zip".equals(suffix)) {
ZipFile zipProp = new ZipFile(file, Charset.forName("GBK"));
for (Enumeration entries = zipProp.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement(); //导致问题所在
String zipEntryName = entry.getName();
if (zipEntryName.endsWith(".json")) {
String jsonSuffix = zipEntryName.substring(zipEntryName.lastIndexOf("."));
String uuid = zipEntryName.substring(zipEntryName.lastIndexOf("/") + 1, zipEntryName.lastIndexOf("."));
String zipdir = zipEntryName.substring(0, zipEntryName.lastIndexOf("/"));
log.info("zipEntryName:【{}】,uuid:【{}】,jsonSuffix:【{}】,zipdir:【{}】", zipEntryName, uuid, jsonSuffix, zipdir);
InputStream input = zipProp.getInputStream(entry);
final int bufferSize = 1024;
final char[] buffer = new char[bufferSize];
final StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(input, "UTF-8");
for (; ; ) {
int rsz = in.read(buffer, 0, buffer.length);
if (rsz < 0)
break;
out.append(buffer, 0, rsz);
}
input.close();
String iosJson = out.toString().trim();
TemplateJsonConverter templateJsonConverter = new TemplateJsonConverter();
String androidJson = templateJsonConverter.transformeParse(uuid, iosJson);
String transSaveFilePath = saveDir + uuid + "_android" + jsonSuffix;
log.info("生成安卓json文件名:{}", transSaveFilePath);
File transFile = new File(request.getSession().getServletContext().getRealPath(transSaveFilePath));
FileWriter fileWriter = new FileWriter(transFile);
fileWriter.write(androidJson);
File unzip = ZipUtil.unzip(file);
log.info(unzip.getPath() + "/" + zipdir);
FileUtil.copy(transFile,new File(unzip.getPath() + "/" + zipdir), false);
ZipUtil.zip(unzip);
log.info("压缩完成");
// transFile.delete(); //需要删除安卓的json
// unzip.delete(); //删除解压的文件
/**
* 问题出现的关键处,将文件覆盖后退出for循环
*/
break;
}
}
}
String ret = VdfsUploadUtility.uploadFile(request.getSession().getServletContext().getRealPath(saveFilePath), saveFile);
}