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

SpringBoot移动文件至Azure存储容器后无法读取文件问题

问题根因

当前代码存在4个核心问题,直接导致读取失败、认证报错:

  • SAS令牌作用域错误:生成的SAS仅对临时容器内的源文件生效,文件移动到永久容器、源文件被删除后,该SAS既不匹配目标文件的位置,也没有永久容器的访问权限,用它拼接目标文件URL访问必然报认证失败。
  • 异步复制未等待完成:Azure Blob的beginCopy是异步操作,调用后仅会发起复制请求,不会等待文件复制完成。当前代码发起复制后立刻删除源文件、读取目标文件,此时目标文件可能还未生成、或复制未完成,自然无法读取。
  • 文件读取API误用:java.io.File类仅支持访问本地文件系统的文件,传入HTTP/HTTPS格式的Blob URL根本无法读取远程资源,这是抛出Can't read input file异常的直接原因。
  • 缩略图存储逻辑错误:生成缩略图后直接写入本地File对象,文件仅存在于应用服务器本地磁盘,并未上传回Azure Blob永久容器,业务侧根本无法访问到生成的缩略图。
修复步骤

1. 修正文件复制逻辑,等待复制完成后再操作

替换moveFileToNewPlace方法中的复制逻辑,阻塞等待复制完成后再删除源文件,避免复制未完成就操作目标文件:

// 原代码 destBlobClient.beginCopy(String.format("%s?%s", srcBlobClient.getBlobUrl(), sasToken), null);
// 替换为以下逻辑
SyncPoller<BlobCopyInfo, Void> copyPoller = destBlobClient.beginCopy(
        String.format("%s?%s", srcBlobClient.getBlobUrl(), sasToken),
        null
);
// 等待复制完成,超时时间可根据业务最大文件大小调整
copyPoller.waitForCompletion(Duration.ofMinutes(3));
// 确认复制成功后再删除源文件
srcBlobClient.delete();

需要额外导入依赖:java.time.Duration、com.azure.core.util.polling.SyncPoller、com.azure.storage.blob.models.BlobCopyInfo

2. 重写缩略图生成逻辑,直接通过Azure SDK读取Blob流

不要用new File(URL)的方式读远程文件,直接通过Blob客户端打开输入流读取图片,跳过SAS拼接、远程URL读取的问题,同时优化缩放质量:

public BufferedImage generateImageThumbnail(BlobClient blobClient) throws IOException {
    // 直接通过SDK鉴权读取Blob流,不需要依赖SAS
    try (InputStream blobInputSteam = blobClient.openInputStream()) {
        BufferedImage originalImg = ImageIO.read(blobInputSteam);
        if (originalImg == null) {
            throw new IOException("图片格式不支持,无法解析");
        }
        BufferedImage thumbnail = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = thumbnail.createGraphics();
        // 配置缩放参数,避免缩略图模糊
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(originalImg, 0, 0, 300, 300, null);
        graphics.dispose();
        return thumbnail;
    }
}

需要额外导入依赖:java.awt.image.BufferedImage、java.awt.Graphics2D、java.awt.RenderingHints、java.io.InputStream、java.io.IOException

3. 修正缩略图上传逻辑,不要存储在本地

替换moveFilesToNewContainer方法中图片处理块的逻辑,生成缩略图后直接上传回永久Blob容器,不要写入本地磁盘:

// 原if (CommonUtils.isImageType(extension)) 块内的逻辑全部替换为以下代码
if (CommonUtils.isImageType(extension)) {
    // 构造缩略图的Blob路径
    String thumbBlobName = String.format("%s/%s_preview%s",
            savedOrder.getOrderNo(),
            CommonUtils.removeFileExtension(fileName),
            extension);
    // 获取原文件、缩略图对应的Blob客户端
    BlobClient originalBlobClient = destContainer.getBlobClient(
            String.format("%s/%s", savedOrder.getOrderNo(), fileName)
    );
    BlobClient thumbBlobClient = destContainer.getBlobClient(thumbBlobName);
    
    // 生成缩略图
    BufferedImage thumbnail = generateImageThumbnail(originalBlobClient);
    // 缩略图转字节流直接上传到Blob容器
    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
        ImageIO.write(thumbnail, extension.replace(".", ""), outputStream);
        byte[] thumbBytes = outputStream.toByteArray();
        // 第三个参数为true表示覆盖已存在的同名文件
        thumbBlobClient.upload(new ByteArrayInputStream(thumbBytes), thumbBytes.length, true);
    }
}

需要额外导入依赖:java.io.ByteArrayInputStream、java.io.ByteArrayOutputStream、com.azure.storage.blob.BlobClient

注意事项

如果后续业务需要通过URL直接访问Blob文件,不要复用源文件的SAS,需要针对目标容器内的具体Blob单独生成对应权限、有效期的SAS,跨容器、跨文件的SAS完全不通用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:01:08