How to Download Files in Spring Boot

In this example, we will show you how to download files via a REST API in Spring Boot.

In the following example controller, we are using StreamingResponseBody interface that is capable of writing data to the response OutputStream directly for downloads:


import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@RestController
@RequestMapping(path = "/api")
public class DownloadFileExampleController {

	@RequestMapping(path = "/downloads/{fileId}")
	public StreamingResponseBody downloadFile(HttpServletResponse response,
			@PathVariable(name = "fileId") String fileId) {
        
		//file path is usually store in a database and is retrieved by id
		String path = "C:/Documents/Videos/video_id_123.mp4";
		
		File file = new File(path);

		response.setContentLength((int) file.length());
		response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
		int BUFFER_SIZE = 1024;

		return outputStream -> {
			int bytesRead;
			byte[] buffer = new byte[BUFFER_SIZE];
			
			//convert the file to InputStream
			InputStream inputStream = new FileInputStream(file);
			while ((bytesRead = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, bytesRead);
			}

			if (inputStream != null) {
				inputStream.close();
			}
		};

	}
}

It is recommended that you explicitly configure the TaskExecutor if the file to be downloaded is large and will take more than a minute to download.

Lets create AsyncConfiguration class and configure the TaskExecutor. Here is the complete code for configuring the TaskExecutor with a request timeout of 3600000 milliseconds (60 minutes). In addition, the code registers an interceptor that is called when a request timeout occurs for handling special cases.


import java.util.concurrent.Callable;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.TimeoutCallableProcessingInterceptor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {

	@Override
	@Bean(name = "taskExecutor")
	public AsyncTaskExecutor getAsyncExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(10);
		executor.setMaxPoolSize(15);
		executor.setQueueCapacity(50);
		return executor;
	}

	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return new SimpleAsyncUncaughtExceptionHandler();
	}

	@Bean
	public WebMvcConfigurer webMvcConfigurerConfigurer(AsyncTaskExecutor taskExecutor,
			CallableProcessingInterceptor callableProcessingInterceptor) {
		return new WebMvcConfigurer() {
			@Override
			public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
				configurer.setDefaultTimeout(3600000).setTaskExecutor(taskExecutor);
				configurer.registerCallableInterceptors(callableProcessingInterceptor);
				WebMvcConfigurer.super.configureAsyncSupport(configurer);
			}
		};
	}

	@Bean
	public CallableProcessingInterceptor callableProcessingInterceptor() {
		return new TimeoutCallableProcessingInterceptor() {
			@Override
			public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
				return super.handleTimeout(request, task);
			}
		};
	}
}