美文网首页SpringBoot整合
SpringBoot实现azure blob的文件上传

SpringBoot实现azure blob的文件上传

作者: 永动的图灵机 | 来源:发表于2020-03-25 19:46 被阅读0次

    Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案。 Blob 存储最适合存储巨量的非结构化数据

    准备

    Azure 订阅

    点击创建免费帐户,选择免费开始,使用微软账户注册订阅后即可试用12个月

    Azure 存储帐户

    点击创建存储帐户,根据教程即可创建一个存储账户,若没有安装azure cli,推荐直接参考【门户网站】一栏

    Azure门户凭据

    1. 登录到 Azure 门户
    2. 找到自己的存储帐户。
    3. 在存储帐户概述的“设置”部分,选择“访问密钥”。 在这里,可以查看你的帐户访问密钥以及每个密钥的完整连接字符串。
    4. 找到“密钥 1”下面的“连接字符串”值,选择“复制”按钮复制该连接字符串。 下一步需将此连接字符串值添加到某个环境变量。
    image

    开发步骤

    配置

    • 引入依赖
    <!--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】,然后从本地选择若干图片

    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实战之文件上传存入Azure Storage

    相关文章

      网友评论

        本文标题:SpringBoot实现azure blob的文件上传

        本文链接:https://www.haomeiwen.com/subject/konxuhtx.html