问题描述
问题描述:在上传大文件时,返回了504 time out
阿里SLB负载有一个参数 ‘连接请求超时时间’,这个时间最大是180s,也就是说slb负载在等待服务端返回结果不能超过180s否则会向客户返回504错误。
经过排查发现文件经过NG转发到TOMCAT容器时,TOMCAT容器会接收文件并写入磁盘(临时文件),发现写临时文件的速度太慢,测试观察到差不多是3-4m/s,所以以导致了超时,
这个是一个客户的环境,它是上传完后才开始写临时文件,,公司另一个测试环境,没有使用slb,是边上传边写临时文件,具体是什么原因这边也没有细细去查了,这个是客户的环境,不能直接连到服务的ng
另一个客户也是使用了 slb+ng 的环境,也是上传到100%后才开始写临时文件,但它写的速度快,就没有出现超进的原因,估计服务器或什么配置有问题,再说一次,因为是客户的环境,这边只能用然后跳转到这台服务器,不好测试,哈哈。
但检测又发现没有什么问题,具体要怎么测试目前也没有什么办法,这里记录一下。
因为时间问题最先的解决方案是向阿里提工单将 ‘连接请求超时时间’ 调长,但反馈说不能设长,这就比较尴尬,网上也是说提工单是可以设置的,估计是现在严了,不允许了。
https://blog.csdn.net/weixin_40581617/article/details/80050178
所以现在的解决方案是对文件进行分割上传后端进行组合
代码实现
前端代码
function submitForm(begin,part){
var file = $('#xlsFile')[0].files[0];
//console.log(file);
const LENGTH = 1024 * 1024 * 10;//每次上传的大小
var totalSize = file.size; //文件总大小
var start = begin; //每次上传的开始字节
var end = start + LENGTH; //每次上传的结尾字节
var blob = null; //二进制对象
blob = file.slice(start,end); //截取每次需要上传字节数
formData = new FormData(); //每一次需重新创建
formData.append('file',blob); //添加数据到表单对象中
formData.append('fileName',file.name);//上传文件的名字
formData.append('part',part); //上传第几部分
$.ajax({
url: '${path}/exe/doUploadDatas',
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false
}).done(function(result){
if(result.code==200){
start = end; // 累计上传字节数
end = start + LENGTH; // 由上次完成的部分字节开始,添加下次上传的字节数
part++; // 上传文件部分累计
if(start>=totalSize){ //如果上传字节数大于或等于总字节数,结束上传
alert('上传完成!');
//告诉后台上传完成后合并文件 //返回上传文件的存放路径
$.post('${path}/exe/mergetFile',{fileName:file.name,path:result.path},function(result){
if(result.code==200){
alert('合并完成!');
}
});
}else{
submitForm(start,part); // 上传字节不等与或大于总字节数,继续上传
}
}
}).fail(function() {
});
}
后端代码
@RequestMapping(value = "/doUploadDatas",method = RequestMethod.POST)
@ResponseBody //synchronized
public Map doSaveImportDatas(@RequestParam("file") MultipartFile file,String fileName,Integer part) throws Exception{
Map map = new HashMap();
try {
//创建存放合并文件 根据需求修改
String path = "D:\\新建文件夹\\source\\temp";
File mkdir = new File(path);
if (!mkdir.exists()) {
mkdir.mkdirs();
}
file.transferTo(new File(path+"\\"+fileName+"-"+part));
map.put("code",200);
map.put("path",path);
} catch (Exception e) {
map.put("error",e.getMessage());
}
return map;
}
@RequestMapping("/mergetFile")
@ResponseBody
public Map mergetFile(String fileName,String path){
Map map = new HashMap();
try {
//合并目标路径 根据需求修改 合并文件名 合并来源路径
merge("D:\\新建文件夹\\target\\", fileName, path);
map.put("code",200);
} catch (Exception e) {
map.put("error",e.getMessage());
}
return map;
}
public static void merge(String targetPath,String targetFileName,String sourcePath) throws IOException{
File file = new File(sourcePath);
File[] fileArr = file.listFiles();
String[] arr = new String[fileArr.length];
//这里要注意,要按文件上传完成顺序合并,如果没有按顺序合并。最终的文件是损坏的。
for (int i = 0; i < fileArr.length; i++) {
arr[i] = fileArr[i].getPath();
}
//根据最后面的部分 按先后顺序排序
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-1; j++) {
if(Integer.parseInt(arr[i].substring(arr[i].lastIndexOf("-"),arr[i].length()))
>Integer.parseInt(arr[j].substring(arr[j].lastIndexOf("-"),arr[j].length()))) {
String temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
File destFile = new File(targetPath+targetFileName);//合并后的文件
InputStream in = null;
OutputStream out = new FileOutputStream(destFile);
try {
byte[] bytes = new byte[1024*1024];
for (String f : arr) {
in = new FileInputStream(f);
System.out.println(f);
int len = -1;
while((len = in.read(bytes))!=-1){
out.write(bytes, 0, len);
}
}
in.close();
out.close();
//删除合并前文件
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
参考连接:https://www.cnblogs.com/nmnm/p/8038791.html