Java实现多线程上传文件

以下是实现多线程上传文件的几种方式:

1. 使用 CompletableFuture 实现(推荐)

private void uploadFileBatch(String id, MultipartFile[] files, String type, SysUser user) throws IOException {
    long st = System.currentTimeMillis();
    log.info("目标管理 开始上传文件 : {}", id);
    
    // 创建 CompletableFuture 列表
    List<CompletableFuture<Void>> futures = Arrays.stream(files)
            .map(file -> CompletableFuture.runAsync(() -> {
                try {
                    uploadFile(id, file, type, user);
                } catch (IOException e) {
                    log.error("文件上传失败: {}", file.getOriginalFilename(), e);
                    throw new RuntimeException(e);
                }
            }))
            .collect(Collectors.toList());
    
    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .join();
    
    long ed = System.currentTimeMillis();
    log.info("上传文件到MinIO成功, 耗时 {} s", (ed - st) / 1000);
}

2. 使用线程池 + CountDownLatch

// 在类中定义线程池
private final ExecutorService uploadExecutor = Executors.newFixedThreadPool(10);

private void uploadFileBatch(String id, MultipartFile[] files, String type, SysUser user) throws IOException {
    long st = System.currentTimeMillis();
    log.info("目标管理 开始上传文件 : {}", id);
    
    CountDownLatch latch = new CountDownLatch(files.length);
    
    for (MultipartFile file : files) {
        uploadExecutor.submit(() -> {
            try {
                uploadFile(id, file, type, user);
            } catch (IOException e) {
                log.error("文件上传失败: {}", file.getOriginalFilename(), e);
            } finally {
                latch.countDown();
            }
        });
    }
    
    try {
        // 等待所有任务完成,设置超时时间
        boolean completed = latch.await(5, TimeUnit.MINUTES);
        if (!completed) {
            log.warn("文件上传超时,可能还有任务未完成");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        log.error("文件上传被中断", e);
    }
    
    long ed = System.currentTimeMillis();
    log.info("上传文件到MinIO成功, 耗时 {} s", (ed - st) / 1000);
}

3. 使用 Spring 的 @Async 注解

3.1 配置异步支持

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean("uploadTaskExecutor")
    public Executor uploadTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("upload-thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }
}

3.2 创建异步服务

@Service
public class AsyncUploadService {
    
    @Async("uploadTaskExecutor")
    public CompletableFuture<Void> uploadFileAsync(String id, MultipartFile file, String type, SysUser user) {
        try {
            // 这里调用你的 uploadFile 方法
            // uploadFile(id, file, type, user);
            return CompletableFuture.completedFuture(null);
        } catch (Exception e) {
            log.error("异步上传文件失败: {}", file.getOriginalFilename(), e);
            return CompletableFuture.failedFuture(e);
        }
    }
}

3.3 在批量上传中使用

@Autowired
private AsyncUploadService asyncUploadService;

private void uploadFileBatch(String id, MultipartFile[] files, String type, SysUser user) throws IOException {
    long st = System.currentTimeMillis();
    log.info("目标管理 开始上传文件 : {}", id);
    
    List<CompletableFuture<Void>> futures = Arrays.stream(files)
            .map(file -> asyncUploadService.uploadFileAsync(id, file, type, user))
            .collect(Collectors.toList());
    
    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .exceptionally(ex -> {
                log.error("部分文件上传失败", ex);
                return null;
            })
            .join();
    
    long ed = System.currentTimeMillis();
    log.info("上传文件到MinIO成功, 耗时 {} s", (ed - st) / 1000);
}

4. 带进度监控的多线程上传

private void uploadFileBatch(String id, MultipartFile[] files, String type, SysUser user) throws IOException {
    long st = System.currentTimeMillis();
    log.info("目标管理 开始上传文件 : {}", id);
    
    // 使用线程安全的计数器
    AtomicInteger successCount = new AtomicInteger(0);
    AtomicInteger failCount = new AtomicInteger(0);
    CountDownLatch latch = new CountDownLatch(files.length);
    
    List<CompletableFuture<Void>> futures = Arrays.stream(files)
            .map(file -> CompletableFuture.runAsync(() -> {
                try {
                    uploadFile(id, file, type, user);
                    successCount.incrementAndGet();
                    log.info("文件上传成功: {}", file.getOriginalFilename());
                } catch (Exception e) {
                    failCount.incrementAndGet();
                    log.error("文件上传失败: {}", file.getOriginalFilename(), e);
                } finally {
                    latch.countDown();
                }
            }))
            .collect(Collectors.toList());
    
    try {
        // 等待所有任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .orTimeout(10, TimeUnit.MINUTES) // 设置超时
                .join();
    } catch (Exception e) {
        log.error("批量上传出现异常", e);
    }
    
    long ed = System.currentTimeMillis();
    log.info("上传文件到MinIO成功, 成功: {}个, 失败: {}个, 总耗时: {}s", 
            successCount.get(), failCount.get(), (ed - st) / 1000);
}

5. 完整的导入包列表

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

推荐方案

推荐使用方案1(CompletableFuture),因为:

  • 代码简洁,函数式编程风格
  • 内置异常处理机制
  • 无需手动管理线程池
  • 可以方便地添加超时控制

注意事项

  1. 线程池配置:根据系统资源合理设置线程数
  2. 异常处理:确保单个文件上传失败不影响其他文件
  3. 资源清理:确保文件流正确关闭
  4. 超时控制:避免长时间等待
  5. 内存管理:大文件上传时注意内存使用

选择最适合你项目需求的方案即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BirdMan98

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值