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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:36:02