Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案。 Blob 存储最适合存储巨量的非结构化数据html
点击建立免费账户,选择免费开始,使用微软帐户注册订阅后便可试用12个月java
点击建立存储账户,根据教程便可建立一个存储帐户,若没有安装azure cli,推荐直接参考【门户网站】一栏spring
<!--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()));
}
}
复制代码
@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);
}
}
复制代码
【请求体】-【form-data】-【key=file】,而后从本地选择若干图片浏览器
为方便测试,直接使用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);
}
}
复制代码
参考文章:app