如何通过Java REST控制器实现S3链接重定向并保留下载文件名
问题根因
你写的Content-Disposition头没有生效,原因是这些头是附加在你服务端返回的301重定向响应上的。浏览器收到3xx重定向响应后,会直接跳转至Location指定的S3地址,此时浏览器只会使用S3返回的响应头,完全忽略重定向响应本身携带的Content-Disposition、Content-Type等头,所以你设置的文件名不会生效。
可行解决方案(无需代理下载文件,完全满足直接重定向的需求)
你不需要将文件下载到自有服务器再返回,只需要在生成S3访问链接的时候,主动指定S3返回响应的头参数即可,S3原生支持该能力:
- 公开访问的S3对象:直接在访问链接后拼接
response-content-disposition和response-content-type查询参数 - 私有访问的S3对象:生成预签名URL时,直接指定这两个响应覆盖参数
代码修改示例(以AWS Java SDK v2为例)
你需要修改storageServiceS3.getUrl(uuid)的实现逻辑,加上响应头覆盖参数:
// 生成预签名URL时指定响应头 public String getUrl(UUID uuid, String fileName) { // 先编码文件名,防止中文、特殊字符出错 String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) .replace("+", "%20"); String contentDisposition = "attachment; filename=\"" + encodedFileName + "\""; String contentType = new MimetypesFileTypeMap().getContentType(fileName); GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket("你的桶名") .key(uuid.toString()) // 你存在S3上的key .responseContentDisposition(contentDisposition) .responseContentType(contentType) .build(); GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) // 链接有效期可自定义 .getObjectRequest(getObjectRequest) .build(); return presigner.presignGetObject(presignRequest).url().toString(); }
对应接口代码可以简化为:
@GetMapping("/test/{uuid}") public ResponseEntity<Void> getFile(@PathVariable UUID uuid) { try { var name = storageServiceS3.getName(uuid); // 生成带响应头覆盖的S3链接 var url = storageServiceS3.getUrl(uuid, name); return ResponseEntity.status(HttpStatus.FOUND) // 建议用302而不是301,防止浏览器缓存重定向规则后文件名更新不生效 .header(HttpHeaders.LOCATION, url) .build(); } catch (NoSuchKeyException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } }
注意事项
- 文件名包含中文、空格、特殊字符时必须做URL编码,否则会出现乱码
- 如果你的S3是公开访问,不需要预签名,直接在原S3链接后拼接参数即可:
{S3原链接}?response-content-disposition={编码后的disposition值}&response-content-type={编码后的contentType值} - 不建议使用301永久重定向,浏览器会长期缓存重定向规则,后续如果文件名调整可能不会生效,改用302临时重定向更合理
内容的提问来源于stack exchange,提问作者Алексеев станислав
相关产品推荐
相关产品推荐

