【debug log】java启动线程实现异步批量上传文件过程中抛FileNotFoundException异常

前言:本人为还未毕业的实习小菜鸟,出于不泄露公司源码的考虑,以下代码均为简化版示例,其中详细逻辑和异常处理已省略。

 

初始场景:

现有一批量上传文件的接口

@PostMapping("/imgUpload")
public String imgUpload(List<Multipartfile> files) {

    for(Multipartfile file : files) {
        //upload file...
    }

    return "上传成功!";

}

改进需求:

项目组长提到,功能是实现了,但要考虑多种实际情况。比如说客户可能一次性要上传几百几千个文件,总大小超过1G甚至更大,那么上面这个接口会一直到for循环内的逻辑全部执行完后才return。造成的结果就是客户这边发起请求后,浏览器转了半天没反应,而且后台处理的事务对于用户来说是不可见的,更何况一般网站还得设置请求超时限制,所以这个简单的批量上传接口必须优化:使后台业务不会堵塞在前端,而且要有操作提示,让用户知道目前是什么情况。

 

正文开始:

先是“后台业务处理不堵塞前端”这个需求,首先想到的肯定就是利用多线程:让线程来执行具体的批量上传逻辑,Controller的方法在启动完该线程后就直接返回响应。(至于线程内如何给前端返回操作提示和实时信息,这个是题外话,以后有机会再写一篇博客记录)

于是有了以下改进版代码:

//批量上传文件的线程
class ImageUploadThread extends Thread {

    private List<Multipartfile> files;

    public void setFiles(List<Multipartfile> files){
        this.files = files;
    }

    @Override
    public void run(){
        
        for(Multipartfile file : files){
            //upload file...
            System.out.println("[线程]文件" + file.getName() + "已上传。");
        }
        System.out.println("[线程]文件上传完成");

    }

}
//请求调用的接口
@PostMapping("/imgUpload")
public String imgUpload(List<Multipartfile> files) {

    ImageUploadThread imgThread = new ImageUploadThread();
    imgThread.setFiles(files);
    imgThreaD.start();

    return "已启动上传文件线程。";

}

开始测试,但输出结果为:

已启动上传文件线程。
[线程]文件img1.jpg已上传。
[线程]文件img2.jpg已上传。
[线程]文件img3.jpg已上传。
[线程]文件img4.jpg已上传。
java.io.FileNotFoundException: ...(找不到某个xxxxx00001.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00002.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00003.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00004.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00005.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00006.tmp文件,省略若干报错信息)
java.io.FileNotFoundException: ...(找不到某个xxxxx00007.tmp文件,省略若干报错信息)
...
[线程]文件img5.jpg已上传。
[线程]文件img6.jpg已上传。
[线程]文件img7.jpg已上传。
[线程]文件img8.jpg已上传。

查看上传文件目录(我的情景是上传到阿里云oss),发现虽然提示信息里所有文件的输出语句都执行了,但上传目标目录里只有孤零零一个img1.jpg

 

经百度,首先得知抛FileNotFoundException的原因是:服务器在接收到带文件的请求时会首先把文件拷贝一份临时tmp到web容器目录下,这也是List<MultipartFile> files得以处理的原因。

但在上述情景中,我的Controller方法在把files作为参数传给Thread类并启动线程后就return了,而Controller方法一return,就视为一次请求-响应已结束,所以web容器里存放临时文件的目录也就被清空了,上面抛的找不到文件异常自然就是找不到这些临时文件。解决方法是使用流作为传入参数。

 

网上案例都是单个文件异步上传,不过道理是通用的,以下为本人修改后的批量异步上传代码示例:

//修改后的批量上传文件线程
class ImageUploadThread extends Thread {

    private Map<String, InputStream> streams;//之前的文件集合list改为文件流集合map

    public void setStreams(Map<String, InputStream> streams){
        this.streams = streams;
    }

    @Override
    public void run(){
        
        for(Map.Entry<String, InputStream> stream : streams.entrySet()){//遍历Map
            //upload file...
            System.out.println("[线程]文件" + stream.getKey() + "已上传。");
        }
        System.out.println("[线程]文件上传完成");

    }

}
//修改后的请求调用接口
@PostMapping("/imgUpload")
public String imgUpload(List<Multipartfile> files) {

    Map<String, InputStream> streams = new HashMap<>();

    if(!files.isEmpty) {
        for(Multipartfile file : files) {
            stream.put(file.getOriginalFileName, file,getInputStream);
        }
    }

    ImageUploadThread imgThread = new ImageUploadThread();
    imgThread.setStreams(streams);//传入文件流集合
    imgThreaD.start();

    return "已启动上传文件线程。";

}

将入参改为文件流后即使Controller方法已return,也不会影响线程继续上传文件。(如你所见,Map的遍历是无序的)

已启动上传文件线程。
[线程]文件img2.jpg已上传。
[线程]文件img8.jpg已上传。
[线程]文件img1.jpg已上传。
[线程]文件img5.jpg已上传。
[线程]文件img3.jpg已上传。
[线程]文件img6.jpg已上传。
[线程]文件img7.jpg已上传。
[线程]文件img4.jpg已上传。

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值