我尝试上传文件然后阅读它,一切正常,但是当我在处理程序方法上添加@Async
注释时却没有。
我不希望用户在处理文件之前一直等待。但在放入此注释后,我得到java.lang.IllegalStateException: File has been moved - cannot be read again
异常。会发生什么,我该如何解决这个问题?据我所知,Spring可能只是清除文件,因为请求 - 响应结束并清理它。但不应该@Async
阻止这种情况吗?
示例Spring Boot应用程序:
@SpringBootApplication
@EnableSwagger2
@ComponentScan(value = "hello")
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("/api/.*"))
.build();
}
}
上传控制器:
@RestController
@RequestMapping(value = "/files")
public class FilesController {
@Inject
private Upload upload;
@RequestMapping(method = RequestMethod.POST)
public void addSource(@RequestParam MultipartFile file) throws IOException, InterruptedException {
upload.process(file);
}
}
上传服务:
@Component
public class Upload {
@Async
public void process(MultipartFile file) throws InterruptedException, IOException {
sleep(2000);
System.out.println(new String(IOUtils.readFully(file.getInputStream(), -1, false)));
}
}
现在我得到java.io.FileNotFoundException
。我不确定我在这里失踪了什么。可能我做错了,因为我无法发现任何错误,我认为这是非常常见的用例。
答案 0 :(得分:14)
您无法以您的方式将MultipartFile参数移交给@Async方法。
当“addSource”方法结束时,MultipartFile超出范围并释放资源。因此,“process”方法中的访问将失败。你通过这种方式构建某种竞赛条件。 Springs DispatcherServlet使用StandardServletMultipartResolver.cleanupMultipart来清理这些文件。在那里放一个断点,看看何时在重新调用addSource(...)时调用此方法。
您应该执行以下操作:将整个文件读入“addSource”方法中的Buffer。然后将缓冲区传递给“进程”方法,让“addSource”返回。
你所谓的“...处理文件......”不是“处理”文件,而是阅读它。
@Async
public void process(byte[] bs){
System.out.println(new String(bs));
//do some long running processing of bs here
}
@RequestMapping(method = RequestMethod.POST)
public void addSource(@RequestParam MultipartFile file) {
upload.process(IOUtils.toByteArray(file));
}