You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Spring集成S3下载遇500异常:InputStream资源无法解析为URL

Fixing FileNotFoundException When Downloading S3 Files via Spring Java

Hey there, let's break down why you're hitting that java.io.FileNotFoundException with the message "resource loaded through InputStream cannot be resolved to URL" when trying to download S3 files via your Spring code—even though manual downloads work fine.

Root Cause

The key issue here is that the InputStream you get from an S3 object is a remote stream, not a file on your local filesystem. Any code that tries to convert this stream into a File or URL (like methods that expect a local file path) will fail because there's no actual local file to resolve to. This is a common mistake when working with remote resources like S3.

Correct Implementation

Instead of trying to map the S3 InputStream to a local file or URL, you should stream the content directly to the client using Spring's built-in response types. Here are two solid approaches:

1. Using InputStreamResource (for smaller files)

This approach wraps the S3 InputStream in an InputStreamResource which Spring can use directly as the response body:

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestHeader;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.S3ObjectInputStream;

@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> downloadFile(@RequestHeader("path") String fileName) throws IOException {
    // Get the S3 object from your service
    S3Object s3Object = s3Service.downloadFile(fileName);
    S3ObjectInputStream inputStream = s3Object.objectContent();
    
    // Extract metadata from S3 to set proper response headers
    String contentType = s3Object.objectMetadata().contentType();
    long contentLength = s3Object.objectMetadata().contentLength();
    
    // Configure download headers
    HttpHeaders headers = new HttpHeaders();
    headers.setContentDispositionFormData("attachment", fileName);
    headers.setContentType(MediaType.parseMediaType(contentType));
    headers.setContentLength(contentLength);
    
    // Return the response with the InputStreamResource
    return ResponseEntity.ok()
            .headers(headers)
            .body(new InputStreamResource(inputStream));
}

2. Using StreamingResponseBody (for larger files)

For bigger files, using StreamingResponseBody is better because it streams the content in chunks instead of loading the entire file into memory:

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.S3ObjectInputStream;

@RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity<StreamingResponseBody> downloadFile(@RequestHeader("path") String fileName) throws IOException {
    S3Object s3Object = s3Service.downloadFile(fileName);
    S3ObjectInputStream inputStream = s3Object.objectContent();
    String contentType = s3Object.objectMetadata().contentType();
    
    HttpHeaders headers = new HttpHeaders();
    headers.setContentDispositionFormData("attachment", fileName);
    headers.setContentType(MediaType.parseMediaType(contentType));
    
    // Stream the content directly to the client
    StreamingResponseBody responseBody = outputStream -> {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        inputStream.close();
    };
    
    return ResponseEntity.ok()
            .headers(headers)
            .body(responseBody);
}

Common Mistakes to Avoid

  • Don't try to convert the S3 InputStream to a File: Methods like FileUtils.copyInputStreamToFile create unnecessary local temp files and can cause scalability/cleanup issues.
  • Don't use URL-based resource loaders: Spring's ResourceLoader or ClassPathResource are designed for local resources, not remote streams.
  • Always close the S3InputStream: While Spring handles closing the response resources, using try-with-resources for manual stream handling helps avoid resource leaks.

内容的提问来源于stack exchange,提问作者AlexK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:18:14