res: (respnse) => {
console.info(respnse.result.active)
let url = respnse.result.active == 'dev'
?'http://10.68.105.145:8081/sxhblh-boot/trainmanage/trainInfo/visitImg?imgName=' + respnse.result.url
:'http://ctg.com.cn:8081/sxhblh-boot/trainmanage/trainInfo/visitImg?imgName=' + respnse.result.url
return url
},
@GetMapping("visitImg")
@ResponseBody
public synchronized void visitImg(String imgName,HttpServletResponse response){
String path;
InputStream inputStream = null;
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
path = "img";
inputStream = FtpClientUtil.readFile(imgName,path);
bufferedInputStream = new BufferedInputStream(inputStream);
bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());
byte[] buf = new byte[1024 * 1024];
int length = 0;
while ((length = bufferedInputStream.read(buf)) != -1) {
bufferedOutputStream.write(buf, 0, length);
bufferedOutputStream.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
如第一段代码所示,这是上传图片后的回调,给图片设置src,可以看到,所有的图片访问调用的都是同一个接口visitImg,参数为imgName,现在的问题是,上传多个图片后,始终只能显示第一个图片,后面的图片都无法显示,后台报错stream closed流关闭,但是我打了断点之后,会进多次断点,并且图片都能显示,然后我在想是不是打了断点之后,上一次请求阻塞下一次请求,使得本来所有图片的访问从并行变为了串行,由此思考我想到了一个解决办法,给方法加上锁synchronized,问题解决了。问题是解决了,但是问题的本质我还是没有弄懂,1、为什么并行访问接口会导致stream closed流被关闭,2、还有没有更好的解决办法(一开始还想在前端设置请求队列,但是请求是以src的形式,所以放弃了),还请各位大神们赐教
需要加上 synchronized ,是因为 Controller 类是单例,所有浏览器访问这个请求方法时,都会调用这个类的实例的。
但 Tomcat 是开启多个线程处理浏览器请求的,所以这个类是多线程环境下,需要安全同步措施。
如果恰好文件名称相同 FtpClientUtil 这个获取文件方法可能也存在多线程同步问题,目标对象为同一个文件的时候,可能会出现 StreamClose 这个要看异常才知道是什么流关闭。
加上断点后后面请求被迫暂停,所以本质是是顺序的。Web 请求一般都是后台做并发控制的,synchronize 加在整个方法上,优化的话可以缩小加锁粒度,只在文件处理那段代码上加锁。
FtpClientUtil.这个类你自己写的?有没有同步,是否是线程安全的。
检查代码里是不是两个线程访问同一个流了。