使用 SpringMVC, 在做一个图片上传功能时遇到一个比较奇怪的问题,记录一下,请大佬多指教。
功能描述
点击"上传"按钮,选择一张图片,点击"提交"按钮,提交到后台,保存到服务器"web-app/upload/image"目录下,将文件路径保存至数据库,成功后返回该路径到前台,JS 写入 img 的 src 属性显示。
问题描述
后台 Java 代码依次执行保存图片、写入路径到DB、返回 AjaxResponse 信息。前台 ajax 在 success 回调函数中将 Java 后台返回的路径写入 img 的 src 属性。
报错 404。无法找到指定资源。
手动复制资源路径可以打开图片。
主要代码(非完整,但基本能看明白意思)
$.ajax({
type : $form.attr('method') || 'POST',
url : $form.attr('action'),
data : new FormData($form[0]),
cache : false,
dataType : 'json',
contentType : false,
processData : false,
success : function(json) {
console.info(json);
if (json.statusCode === 200) {
//sleep(2000);
//console.info("ajax success: "+new Date().getTime());
$('#headImg').attr('src', json.map.headImg+"?v="+Math.random());
} else {
}
},
error : function(e) {
console.error(e);
}
/*获取文件信息*/
public UploadFileManager getFiles(HttpServletRequest request) {
if (request instanceof MultipartRequest == false) {
request = new MultipartRequest(request);
}
return ((MultipartRequest)request).getFileManager();
}
/*上传文件*/
@RequestMapping("saveHeadImg")
@ResponseBody
public AjaxResponse savaHeadImg(HttpServletRequest request) {
AjaxResponse response = AjaxResponseUtils.getFailureResponse();//初始化返回信息。默认失败 STATUS = 300
//获取上传文件管理器
UploadFileManager fileManager = getFiles(request);
UploadFile file = fileManager.getFile();
fileManager.save();
//System.out.println("file save success: "+System.currentTimeMillis());
//保存上传路径到数据库
Users update = new Users();
update.setId(1);
update.setHeadImg(file.getCompleteName());
this.userService.update(update);
//System.out.println("insert db success: " + System.currentTimeMillis());
response = AjaxResponseUtils.getSuccessResponse();//保存成功返回信息。STATUS = 200
response.put("headImg", file.getCompleteName());//写入图片路径
//System.out.println("return : " + System.currentTimeMillis());
return response;
}
调试过程
1、正常运行时报错找不到资源。
GET http://localhost:8080/Maven4Web/upload/201905157897d8075d944702a32b61d04426944e.jpg?v=0.24819909817403518 404 (Not Found)
2、直接复制 url 到地址栏打开可以正常访问图片
这里初步判断可能是因为代码执行顺序问题导致前端页面访问图片地址时,服务器端的图片还未上传成功?
3、Java 代码中断点调试发现执行顺序正常。先保存图片到服务器(文件夹中已经可以看到文件),保存成功后写入路径到数据库,最后返回 response 对象。
也就是说,JS 代码 ajax 请求中的 success 回调方法执行时,图片已经是确定成功保存在服务器目录中的。那为什么无法直接显示呢?
4、JS 代码 ajax 请求 success 回调函数中断点调试。发现也可以正常显示图片?
这个现象很明显说明,在 success 回调方法中需要短暂的停顿后才可以正常加载图片。
5、为了验证猜想,在 success 方法中调用自定义的 sleep 方法,目的是让其休息 2 秒钟。运行,发现确实可以正常加载上传的图片
img3
这说明,猜想似乎距离真相不远了。
以前也并非没有碰到过类似问题。只是,以前碰到类似问题,大多原因都是由于异步或者多线程等操作导致方法执行完成的顺序错乱。
比如,图片上传、保存到DB、页面加载图片路径这几个方法异步执行,可能会导致页面加载时图片还未保存完成,出现上述现象。
但这次的代码,后台并未进行多线程或异步操作,前端虽然是 Ajax 提交,但也是在后台执行完成后才触发 success 回调从而加载图片路径。
所以应该另有其他原因。
6、为了进一步确认方法触发的时间顺序,在前后端代码都分别加入了时间戳打印。先屏蔽 JS 中的 sleep 方法。运行。
信息: Reloading Context with name [/Maven4Web] is completed
file save success: 1557928478910
insert db success: 1557928479468
return : 1557928479468
ajax success: 1557928479524
GET http://localhost:8080/Maven4Web/upload/20190515047df2c0ae094269a790fdfc7dcd06e4.jpg?v=0.9551198420254206 404 (Not Found)
7、解开 sleep 方法。再次运行
file save success: 1557928663064
insert db success: 1557928663146
return : 1557928663146
ajax success: 1557928665157
总结
通过调试,可以确认的是,js 中修改 img 的 src 属性试图加载图片时,图片是确实已经成功保存在该路径下的。
但为什么立即加载图片会失败,延迟加载图片就可以成功?
因本人新手入门,才疏学浅。目前还未确认问题原因,后续还会继续测试,如有大佬知道问题所在,烦请指导一下,不胜感激。