Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案。 Blob 存储最适合存储巨量的非结构化数据准备Azure 订阅点击创建免费帐户,选择免费开始,使用微软账户注册订阅
.
Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案。 Blob 存储最适合存储巨量的非结构化数据
.准备
Azure 订阅
点击创建免费帐户,选择免费开始,使用微软账户注册订阅后即可试用12个月
Azure 存储帐户
点击创建存储帐户,根据教程即可创建一个存储账户,若没有安装azure cli,推荐直接参考【门户网站】一栏
Azure门户凭据
- 登录到 Azure 门户。
- 找到自己的存储帐户。
- 在存储帐户概述的“设置”部分,选择“访问密钥”。 在这里,可以查看你的帐户访问密钥以及每个密钥的完整连接字符串。
- 找到“密钥 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: 1MBazure: 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@Componentpublic 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);}
- 实现类
@Servicepublic 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控制器
@RestControllerpublic 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模板进行页面上传
- 引入依赖
<!-- 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>
- 控制器添加跳转请求
@Controllerpublic 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,选择图片上传
- 上传成功
详细过程,可参考源代码:github.com/chetwhy/clo…
参考文章:
Spring Boot实战之文件上传存入Azure Storage
..

- 0