SpringBoot achieve azure blob file uploads

Azure Blob storage is suitable for cloud-based object storage solution provided by Microsoft. Blob storage for storing the most massive unstructured data

ready

Azure subscription

Click to create a free account to probation for 12 months, free choice to start using Microsoft account Sign up for later

Azure Storage Account

Click to create a storage account , tutorials to create a storage account in accordance with, if not installed azure cli, recommended direct reference to a column [portal]

Azure Portal credentials

  1. Log on to the Azure portal .
  2. Find their own storage account.
  3. "Settings" section, select "access key" as outlined in storage account. Here, you can view your account access key and complete connection string for each key.
  4. Found "Key 1" below "connect string" value, select the "copy" button to copy the connection string. The next step required to connect this string value to an environment variable.

Development steps

Configuration

  • The introduction of dependence
<!--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>
复制代码
  • Configuration Properties
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  # 生成缩略图
复制代码

Coding

  • The class attribute parameter corresponding to prepare
@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;
    }
}
复制代码
  • Preparation of documents uploaded return model
@Data
@Accessors(chain = true)
public class BlobUpload {
    // 文件名
    private String fileName;

    // 原文件
    private String fileUrl;

    // 缩略图
    private String thumbnailUrl;
}
复制代码
  • Tools
/**
 * 获取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;
}
复制代码
  • File uploads written business layer interface
public interface IAzureStorageService {

    /**
     * 上传文件(图片)
     * @param type 文件类型
     * @param multipartFiles 文件
     * @return
     */
    BaseResult<Object> uploadFile(String type, MultipartFile[] multipartFiles);
}
复制代码
  • Implementation class
@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 controller
@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);
    }
}
复制代码

Column real test

Use postman test

[Body] request - form-data [] - [] key = file, and select an image from a plurality of local

  • Copy image url to view pictures

  • View blob container, which can be seen most recently uploaded files

Use the form to submit test

For the convenience of the test, the direct use templates for page upload thymeleaf

  • The introduction of dependence
<!-- thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
复制代码
  • Simple page
<!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>
复制代码
  • Jump controller add request
@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);
    }
}
复制代码
  • Browser to access http: // localhost: 8082 / upload, select the picture to upload

  • Upload successful


Reference article:

Azure Blob storage document

Spring Boot combat the file uploaded into Azure Storage

Guess you like

Origin juejin.im/post/5e104b69f265da5d4727b6b8