
InputStream으로 파일을 읽어드리면 모든 데이터를 한꺼번에 메모리에 적재하지 않고 필요한 만큼만 메모리에 적재하고 처리할 수 있어 메모리 효율적입니다. 이에 대한 얘기를 해보겠습니다.
InputStream의 메모리 효율을 얘기하기 전에, MultipartFile를 살펴보면 좋습니다. 왜냐하면 MultipartFile은 InputStream과 반대로 파일 전체를 한번에 메모리 또는 디스크에 적재하기 때문입니다. 
메모리에 적재할지 또는 디스크에 적재할지는 spring.servlet.multipart.file-size-threshold 속성값에 의해 정해집니다. 해당 값을 초과하는 파일은 디스크에 적재되고, 해당 값보다 작은 파일은 메모리에 적재됩니다. SpringBoot를 사용할 때 file-size-threshold의 기본값은 0으로 기본적으로 모든 파일을 디스크에 적재합니다.
이 디스크란 Spring과 톰캣이 자동으로 관리하는 저장 경로로, System.getProperty("java.io.tmpdir")를 통해 그 경로를 얻을 수 있습니다.
간단한 실습을 통해 MultipartFile의 동작을 살펴봅시다.
@RestController
public class MultipartFileController {
    @PostMapping
    public void getFile(@RequestParam("file") MultipartFile file) {
        System.out.printf("파일 크기 : %.2f KB \n", file.getSize() / 1024.0);
        System.out.println(System.getProperty("java.io.tmpdir"));
        try{
            Thread.sleep(100000);
        }catch(Exception e){
            System.out.println("Err");
        }
    }
}


/var/folders/gg/gf5p6jwj7s5c5w3z9y096yyh0000gn/T/입니다.
/var/folders/gg/gf5p6jwj7s5c5w3z9y096yyh0000gn/T/경로에 여러 폴더가 생성되어 있습니다. 그 중 tomcat.8080.xxx폴더 안으로 쭉 들어가면
.png로 바꿔보면 Postman으로 전송했던 파일과 일치하는걸 확인할 수 있습니다.다음으로 spring.servlet.multipart.file-size-threshold속성값을 사용해 파일을 디스크에 곧바로 적재해 봅시다.
spring:
  servlet:
    multipart:
      file-size-threshold: 100KB

MultipartFile을 메모리에 적재할 때는 파일의 일부분이 아닌 파일 전체를 한번에 적재합니다. 만약 업로드하는 파일의 용량이 크다면 많은 메모리 공간을 차지하게 될 것이고, 이는 OOM등의 문제가 발생할 위험이 있습니다. 이러한 문제를 InputStream을 통해 해결할 수 있습니다.
InputStream은 파일 전체를 한번에 메모리에 적재하지 않고 한번에 버퍼의 크기만큼만 메모리에 적재합니다. 
@RestController
public class InputStreamController {
    @GetMapping
    public void getFile() throws Exception {
        File file = new File("file.txt");
        long fileSize = file.length();
        try (InputStream in = new FileInputStream(file)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            int count = 0;
            long totalRead = 0;
            while ((bytesRead = in.read(buffer)) != -1) {
                totalRead += bytesRead;
                count++;
                System.out.printf("%03d번째 읽기: %d bytes (누적: %.2f%%) \n",
                        count, bytesRead, (totalRead * 100.0 / fileSize));
            }
            System.out.println("전체 읽기 완료");
        }
    }
}

하지만, InputStream으로 읽어드리는 데이터를 어딘가에 저장한다면 이때는 MultipartFile과 마찬가지로 전체 파일이 메모리에 적재됩니다.
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((bytesRead = in.read(buffer)) != -1) {
    out.write(buffer, 0, bytesRead);
}
byte[] result = out.toByteArray();