Java如何无需前端支持实现用户访问API即可跳转Blob Store资源下载
实现方案
核心思路是修改原接口的返回逻辑,不再返回HTML格式的链接文本,而是通过重定向或者服务端代理流的方式,直接将Blob资源返回给浏览器,无需前端参与。
场景1:Blob资源支持公网直接访问
- 实现逻辑:根据
item-id查询到对应Blob的公网访问地址后,返回302临时重定向响应,浏览器会自动跳转访问Blob资源,直接实现预览或下载。 - Spring Boot代码示例:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @RestController @RequestMapping("/api/blobs") public class BlobDownloadController { // 自行实现的Blob存储查询服务 private final BlobStoreService blobStoreService; public BlobDownloadController(BlobStoreService blobStoreService) { this.blobStoreService = blobStoreService; } @GetMapping("/download/{itemId}") public void downloadBlob(@PathVariable String itemId, HttpServletResponse response) throws IOException { // 根据itemId查询Blob公网访问链接 String blobPublicUrl = blobStoreService.getBlobPublicUrl(itemId); // 302重定向到Blob地址 response.sendRedirect(blobPublicUrl); } }
如需控制行为是预览还是强制下载,可在Blob链接后追加对应厂商的响应头参数,比如Azure Blob可加
?rscd=inline指定预览,加?rscd=attachment%3Bfilename%3D自定义文件名.后缀指定强制下载。
场景2:Blob资源为私有,不支持公网直接访问
- 实现逻辑:后端从Blob存储服务拉取资源的字节流,再将流输出到HTTP响应中,同时设置正确的响应头即可。
- Spring Boot代码示例:
import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @RestController @RequestMapping("/api/blobs") public class BlobDownloadController { private final BlobStoreService blobStoreService; public BlobDownloadController(BlobStoreService blobStoreService) { this.blobStoreService = blobStoreService; } @GetMapping("/download/{itemId}") public void downloadBlob(@PathVariable String itemId, HttpServletResponse response) throws Exception { // 1. 查询Blob元数据:文件名、MIME类型、文件大小 BlobMeta blobMeta = blobStoreService.getBlobMeta(itemId); // 2. 从Blob存储拉取资源输入流 InputStream blobInputStream = blobStoreService.getBlobInputStream(itemId); // 3. 设置响应头 // 强制下载设置为二进制流,预览可设置为对应资源的MIME类型,比如图片为image/png、PDF为application/pdf response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 处理中文文件名乱码 String encodedFileName = URLEncoder.encode(blobMeta.getFileName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20"); // 强制下载用attachment,预览替换为inline response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename*=UTF-8''%s", encodedFileName)); response.setContentLengthLong(blobMeta.getSize()); // 4. 输出流到客户端 try (BufferedInputStream bis = new BufferedInputStream(blobInputStream); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = bis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } os.flush(); } } }
注意事项
- 大文件场景优先使用私有签名直链+重定向的方案,避免代理拉取占用服务端带宽。
- 私有签名直链需设置合理的过期时间,避免资源泄露。
内容的提问来源于stack exchange,提问作者Duy
相关产品推荐
相关产品推荐

