SpringBoot實現azure blob的文件上傳

Azure Blob 存儲是 Microsoft 提供的適用於雲的對象存儲解決方案。 Blob 存儲最適合存儲巨量的非結構化數據html

準備

Azure 訂閱

點擊建立免費賬戶,選擇免費開始,使用微軟帳戶註冊訂閱後便可試用12個月java

Azure 存儲賬戶

點擊建立存儲賬戶,根據教程便可建立一個存儲帳戶,若沒有安裝azure cli,推薦直接參考【門戶網站】一欄spring

Azure門戶憑據

  1. 登陸到 Azure 門戶
  2. 找到本身的存儲賬戶。
  3. 在存儲賬戶概述的「設置」部分,選擇「訪問密鑰」。 在這裏,能夠查看你的賬戶訪問密鑰以及每一個密鑰的完整鏈接字符串。
  4. 找到「密鑰 1」下面的「鏈接字符串」值,選擇「複製」按鈕複製該鏈接字符串。 下一步需將此鏈接字符串值添加到某個環境變量。

開發步驟

配置

  • 引入依賴
<!--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  # 生成縮略圖
複製代碼

代碼編寫

  • 根據屬性編寫對應參數類
@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;
}
複製代碼
  • 編寫文件上傳的業務層接口
public interface IAzureStorageService {

    /** * 上傳文件(圖片) * @param type 文件類型 * @param multipartFiles 文件 * @return */
    BaseResult<Object> uploadFile(String type, MultipartFile[] multipartFiles);
}
複製代碼
  • 實現類
@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");
                    }
                    // 時間+隨機數+文件擴展名
                    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("上傳[{}]時出現異常:[{}]", tempMultipartFile.getOriginalFilename(), e.getMessage());
                    return BaseResult.error("上傳出現異常,請稍後再試");
                }
            }
            return BaseResult.success(blobUploadEntities);
        } catch (Exception e) {
            logger.error("上傳文件出現異常: [{}]", e.getMessage());
        }
        return BaseResult.error("上傳出現異常,請稍後再試");
    }

    /** * 判斷批量文件中是否都爲圖片 */
    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】,而後從本地選擇若干圖片瀏覽器

  • 複製圖片url便可查看圖片

  • 查看blob容器,便可以看到最新上傳的文件

使用表單提交測試

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

  • 引入依賴
<!-- 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);
    }
}
複製代碼
  • 瀏覽器訪問http://localhost:8082/upload,選擇圖片上傳

  • 上傳成功


參考文章:app

Azure Blob 存儲文檔dom

Spring Boot實戰之文件上傳存入Azure Storageide

相關文章
相關標籤/搜索