Skip to content
Advertisement

How to iterate over MultipartFile array using Java lambda and streams

Below is my code to upload some file attachments to external storage. I would like to know if there is a way to avoid for loop in the below method to iterate over the MultipartFile[] array so that everything will be done using the java streams and lambda functions. Would like to have any better way to achieve the below

public void uploadMyFiles(MultipartFile[] multipartFiles, String path) throws Exception {
    ConcurrentHashMap<String, String> sMap = new ConcurrentHashMap<>();
    ExecutorService myExecutor = Executors.newFixedThreadPool(5);
    for (MultipartFile multipartFile : multipartFiles) {
        CompletableFuture<String> future = CompletableFuture
                .supplyAsync(() -> uploadMyFile(multipartFile, path), myExecutor );
        String status = future.get(10, TimeUnit.SECONDS);
        sMap.put(multipartFile.getOriginalFilename(), status);
    }
}

  private String uploadMyFile(MultipartFile file, String fpath){
    return null;
  }

Advertisement

Answer

 private static Map<String, String> retrieveCompletableFuture(
      CompletableFuture<Map<String, String>> futureMap) {
    try {
      return futureMap.get(10, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
      e.printStackTrace();
    }
    return null;
  }

  public void uploadMyFiles(MultipartFile[] multipartFiles) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(5);

    String s3Path = "/demo-mypath/";

    ExecutorService myExecutor = Executors.newFixedThreadPool(5);
    Arrays.stream(multipartFiles)
        .map(
            multipartFile ->
                CompletableFuture.supplyAsync(() -> uploadMyFile(multipartFile, s3Path), executor))
        .map(cfuture -> retrieveCompletableFuture(cfuture))
        .map(Map::entrySet)
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

  }

I’ve exactly made your implementation to lambdas. Note that here future.get() is a blocking call, which means these execute sequentially (and hence no need for ConcurrentHashMap).

If you are looking for parallel operations, you’ll need to have a parallelStream. which can submit task and wait. In such case, you’ll need to use Collectors.toConcurrentMap to collect the results. (Be very careful of the race conditions that may arise while merging the streams into single Map)

Advertisement