SpringBoot實現(xiàn)azure blob的文件上傳

Azure Blob 存儲是 Microsoft 提供的適用于云的對象存儲解決方案。 Blob 存儲最適合存儲巨量的非結構化數(shù)據(jù)

準備

Azure 訂閱

點擊創(chuàng)建免費帳戶,選擇免費開始,使用微軟賬戶注冊訂閱后即可試用12個月

Azure 存儲帳戶

點擊創(chuàng)建存儲帳戶,根據(jù)教程即可創(chuàng)建一個存儲賬戶,若沒有安裝azure cli,推薦直接參考【門戶網(wǎng)站】一欄

Azure門戶憑據(jù)

  1. 登錄到 Azure 門戶。
  2. 找到自己的存儲帳戶。
  3. 在存儲帳戶概述的“設置”部分,選擇“訪問密鑰”。 在這里,可以查看你的帳戶訪問密鑰以及每個密鑰的完整連接字符串。
  4. 找到“密鑰 1”下面的“連接字符串”值,選擇“復制”按鈕復制該連接字符串。 下一步需將此連接字符串值添加到某個環(huán)境變量。
image

開發(fā)步驟

配置

  • 引入依賴
<!--lombok -->
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
</dependency>

<!--azure storage -->
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-storage-spring-boot-starter</artifactId>
    <version>0.2.0</version>
</dependency>
  • 屬性配置
spring:
  servlet:      
    multipart:  # spring mvc文件上傳
      max-request-size: 10MB
      max-file-size: 1MB

azure:
  storage: # azure儲存配置
    default-endpoints-protocol: https
    account-name: [account-name]
    account-key: [account-key]
    endpoint-suffix: [endpoint-suffix]
    container-reference: [container-reference]  # 容器名稱
    generate-thumbnail: false  # 生成縮略圖

代碼編寫

  • 根據(jù)屬性編寫對應參數(shù)類
@Data
@Component
public class AzureStorageParam {

    @Value("${azure.storage.default-endpoints-protocol}")
    private String defaultEndpointsProtocol;

    @Value("${azure.storage.account-name}")
    private String accountName;

    @Value("${azure.storage.account-key}")
    private String accountKey;

    @Value("${azure.storage.endpoint-suffix}")
    private String endpointSuffix;

    @Value("${azure.storage.container-reference}")
    private String containerReference;

    /**
     * 拼接連接字符串
     */
    public String getStorageConnectionString() {
        String storageConnectionString =
            String.format("DefaultEndpointsProtocol=%s;AccountName=%s;AccountKey=%s;EndpointSuffix=%s",
                defaultEndpointsProtocol, accountName, accountKey, endpointSuffix);
        return storageConnectionString;
    }
}
  • 編寫文件上傳的返回模型
@Data
@Accessors(chain = true)
public class BlobUpload {
    // 文件名
    private String fileName;

    // 原文件
    private String fileUrl;

    // 縮略圖
    private String thumbnailUrl;
}
  • 工具類
/**
 * 獲取blob container
 * 
 * @param storageConnectionString
 * @param containerReference
 * @return
 */
public static CloudBlobContainer getAzureContainer(String storageConnectionString, String containerReference) {
    CloudStorageAccount storageAccount;
    CloudBlobClient blobClient = null;
    CloudBlobContainer container = null;
    try {
        storageAccount = CloudStorageAccount.parse(storageConnectionString);
        blobClient = storageAccount.createCloudBlobClient();
        container = blobClient.getContainerReference(containerReference);

        container.createIfNotExists(BlobContainerPublicAccessType.CONTAINER, new BlobRequestOptions(),
            new OperationContext());
        return container;
    } catch (Exception e) {
        logger.error("獲取azure container異常: [{}]" , e.getMessage());
    }
    return null;
}
  • 編寫文件上傳的業(yè)務層接口
public interface IAzureStorageService {

    /**
     * 上傳文件(圖片)
     * @param type 文件類型
     * @param multipartFiles 文件
     * @return
     */
    BaseResult<Object> uploadFile(String type, MultipartFile[] multipartFiles);
}
  • 實現(xiàn)類
@Service
public class AzureStorageServiceImpl implements IAzureStorageService {
    // 設置縮略圖的寬高
    private static int thumbnailWidth = 150;
    private static int thumbnailHeight = 100;
    private static String thumbnailPrefix = "mini_";
    private static String originPrefix = "FAQ_";
    private final Logger logger = LoggerFactory.getLogger(AzureStorageServiceImpl.class);

    @Value("{azure.storage.generate-thumbnail}")
    private String generateThumbnail;

    @Autowired
    private AzureStorageParam azureStorageParam;

    @Override
    public BaseResult<Object> uploadFile(String type, MultipartFile[] multipartFiles) {
        // 校驗圖片
        if (hasInvalidPic(multipartFiles)) {
            return BaseResult.error("包含非法圖片格式");
        }

        List<BlobUpload> blobUploadEntities = new ArrayList<>();

        // 獲取blob容器
        CloudBlobContainer container = AzureStorageUtil.getAzureContainer(
            azureStorageParam.getStorageConnectionString(), azureStorageParam.getContainerReference());
        if (container == null) {
            logger.error("獲取azure container異常");
            return BaseResult.error("獲取容器失敗");
        }
        try {
            for (MultipartFile tempMultipartFile : multipartFiles) {
                try {
                    // 將 blob 上傳到容器
                    String contentType = tempMultipartFile.getContentType().toLowerCase();
                    if (!contentType.equals("image/jpg") && !contentType.equals("image/jpeg")
                        && !contentType.equals("image/png")) {
                        return BaseResult.error("not pic");
                    }
                    // 時間+隨機數(shù)+文件擴展名
                    String picType = contentType.split("/")[1];
                    String timeStamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
                    int number = (int)((Math.random() * 9) * 1000);
                    String referenceName = originPrefix + timeStamp + number + "." + picType;
                    CloudBlockBlob blob = container.getBlockBlobReference(referenceName);
                    blob.getProperties().setContentType(tempMultipartFile.getContentType());
                    blob.upload(tempMultipartFile.getInputStream(), tempMultipartFile.getSize());

                    // 返回圖片URL
                    BlobUpload blobUploadEntity = new BlobUpload();
                    blobUploadEntity.setFileName(tempMultipartFile.getOriginalFilename())
                        .setFileUrl(blob.getUri().toString());

                    // 生成縮略圖
                    if ("true".equalsIgnoreCase(generateThumbnail)) {
                        BufferedImage img =
                            new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB);
                        BufferedImage read = ImageIO.read(tempMultipartFile.getInputStream());
                        img.createGraphics().drawImage(
                            read.getScaledInstance(thumbnailWidth, thumbnailHeight, Image.SCALE_SMOOTH), 0, 0, null);
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ImageIO.write(img, "jpg", baos);
                        InputStream bais = new ByteArrayInputStream(baos.toByteArray());

                        String blobThumbnail = originPrefix + thumbnailPrefix + timeStamp + number + ".jpg";
                        CloudBlockBlob thumbnailBlob = container.getBlockBlobReference(blobThumbnail);
                        thumbnailBlob.getProperties().setContentType("image/jpeg");
                        thumbnailBlob.upload(bais, baos.toByteArray().length);

                        blobUploadEntity.setFileUrl(blob.getUri().toString())
                            .setThumbnailUrl(thumbnailBlob.getUri().toString());

                        // 關閉流
                        baos.close();
                        bais.close();
                    }
                    blobUploadEntities.add(blobUploadEntity);
                } catch (Exception e) {
                    logger.error("上傳[{}]時出現(xiàn)異常:[{}]", tempMultipartFile.getOriginalFilename(), e.getMessage());
                    return BaseResult.error("上傳出現(xiàn)異常,請稍后再試");
                }
            }
            return BaseResult.success(blobUploadEntities);
        } catch (Exception e) {
            logger.error("上傳文件出現(xiàn)異常: [{}]", e.getMessage());
        }
        return BaseResult.error("上傳出現(xiàn)異常,請稍后再試");
    }

    /**
     * 判斷批量文件中是否都為圖片
     */
    private boolean hasInvalidPic(MultipartFile[] multipartFiles) {
        List<String> picTypeList = Arrays.asList("image/jpg", "image/jpeg", "image/png");
        return Arrays.stream(multipartFiles).anyMatch(i -> !picTypeList.contains(i.getContentType().toLowerCase()));
    }
}
  • mvc控制器
@RestController
public class UploadController {
    private static final Logger logger = LoggerFactory.getLogger(UploadController.class);

    @Autowired
    AzureStorageServiceImpl azureStorageService;

    /**
     * 文件上傳(圖片)
     * 
     * @param multipartFiles
     * @return
     */
    @PostMapping("/upload")
    public BaseResult<Object> upload(@RequestPart("file") MultipartFile[] multipartFiles) {
        logger.info("開始文件上傳...");
        if (multipartFiles == null || multipartFiles.length == 0) {
            return BaseResult.error("上傳失敗,請選擇文件");
        }

        return azureStorageService.uploadFile("PICTURE", multipartFiles);
    }
}

實列測試

使用postman測試

【請求體】-【form-data】-【key=file】,然后從本地選擇若干圖片

image
  • 復制圖片url即可查看圖片
image
  • 查看blob容器,即可以看到最新上傳的文件
image

使用表單提交測試

為方便測試,直接使用thymeleaf模板進行頁面上傳

  • 引入依賴
<!-- thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • 簡易頁面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多文件上傳</title>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file"><br>
    <input type="file" name="file"><br>
    <input type="file" name="file"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>
  • 控制器添加跳轉請求
@Controller
public class UploadController {
    private static final Logger logger = LoggerFactory.getLogger(UploadController.class);

    @Autowired
    AzureStorageServiceImpl azureStorageService;

    /**
     * 文件上傳,跳轉使用
     */
    @GetMapping("/upload")
    public String upload() {
        return "upload";
    }

    /**
     * 文件上傳(圖片)
     */
    @PostMapping("/upload")
    @ResponseBody
    public BaseResult<Object> upload(@RequestPart("file") MultipartFile[] multipartFiles) {
        logger.info("開始文件上傳...");
        if (multipartFiles == null || multipartFiles.length == 0) {
            return BaseResult.error("上傳失敗,請選擇文件");
        }

        return azureStorageService.uploadFile("PICTURE", multipartFiles);
    }
}
image
  • 上傳成功
image

詳細過程,可參考源代碼:https://github.com/chetwhy/cloud-flow


參考文章:

Azure Blob 存儲文檔

Spring Boot實戰(zhàn)之文件上傳存入Azure Storage

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容