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