Spring Boot实战之文件上传存入Azure Storage

时间:2022-02-23 16:49:27

Spring Boot实战之文件上传存入Azure Storage


本章介绍,文件上传及文件上传至Azure的流程,以上传图片为例

1、本章与Azure的交互使用到Azure storage相关的依赖库,配置pom.xml,下载依赖库

<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>4.0.0</version>
</dependency>

2、添加azure storage的配置信息类StorageConfig,用来配置连接信息

package com.xiaofangtech.sunt.storage;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "azureblob", locations = "classpath:azureblob.properties")
public class StorageConfig {
private String defaultEndpointsProtocol;
private String blobEndpoint;
private String queueEndpoint;
private String tableEndpoint;
private String accountName;
private String accountKey;
public String getDefaultEndpointsProtocol() {
return defaultEndpointsProtocol;
}
public void setDefaultEndpointsProtocol(String defaultEndpointsProtocol) {
this.defaultEndpointsProtocol = defaultEndpointsProtocol;
}
public String getBlobEndpoint() {
return blobEndpoint;
}
public void setBlobEndpoint(String blobEndpoint) {
this.blobEndpoint = blobEndpoint;
}
public String getQueueEndpoint() {
return queueEndpoint;
}
public void setQueueEndpoint(String queueEndpoint) {
this.queueEndpoint = queueEndpoint;
}
public String getTableEndpoint() {
return tableEndpoint;
}
public void setTableEndpoint(String tableEndpoint) {
this.tableEndpoint = tableEndpoint;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountKey() {
return accountKey;
}
public void setAccountKey(String accountKey) {
this.accountKey = accountKey;
}
}


3、新建配置文件azureblob.properties

azureblob.defaultEndpointsProtocol=http
azureblob.blobEndpoint=http://teststorage.blob.core.chinacloudapi.cn/
azureblob.queueEndpoint=http://teststorage.queue.core.chinacloudapi.cn/
azureblob.tableEndpoint=http://teststorage.table.core.chinacloudapi.cn/
azureblob.accountName=teststorage
azureblob.accountKey=accountkey


4、新建帮助类,BlobHelper.java 用来获取或创建Blob所在容器

package com.xiaofangtech.sunt.storage;

import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.BlobContainerPermissions;
import com.microsoft.azure.storage.blob.BlobContainerPublicAccessType;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;

public class BlobHelper {

public static CloudBlobContainer getBlobContainer(String containerName, StorageConfig storageConfig)
{
try
{
String blobStorageConnectionString = String.format("DefaultEndpointsProtocol=%s;"
+ "BlobEndpoint=%s;"
+ "QueueEndpoint=%s;"
+ "TableEndpoint=%s;"
+ "AccountName=%s;"
+ "AccountKey=%s",
storageConfig.getDefaultEndpointsProtocol(), storageConfig.getBlobEndpoint(),
storageConfig.getQueueEndpoint(), storageConfig.getTableEndpoint(),
storageConfig.getAccountName(), storageConfig.getAccountKey());

CloudStorageAccount account = CloudStorageAccount.parse(blobStorageConnectionString);
CloudBlobClient serviceClient = account.createCloudBlobClient();

CloudBlobContainer container = serviceClient.getContainerReference(containerName);

// Create a permissions object.
BlobContainerPermissions containerPermissions = new BlobContainerPermissions();

// Include public access in the permissions object.
containerPermissions.setPublicAccess(BlobContainerPublicAccessType.CONTAINER);

// Set the permissions on the container.
container.uploadPermissions(containerPermissions);
container.createIfNotExists();

return container;
}
catch(Exception e)
{
return null;
}
}
}


5、定义上传成功后,返回的结果类,包含文件名称,上传到azure storage后文件的链接,缩略图链接

package com.xiaofangtech.sunt.storage;

public class BlobUpload {
private String fileName;
private String fileUrl;
private String thumbnailUrl;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
}


6、修改MyUtil.java 添加获取文件MD5值的方法

package com.xiaofangtech.sunt.utils;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;

public class MyUtils {

private static char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
'f' };

public static String getMD5(String inStr) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {

e.printStackTrace();
return "";
}
char[] charArray = inStr.toCharArray();
byte[] byteArray = new byte[charArray.length];

for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];

byte[] md5Bytes = md5.digest(byteArray);

StringBuffer hexValue = new StringBuffer();

for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
}

return hexValue.toString();
}

public static String getMD5(InputStream fileStream) {

try {
MessageDigest md = MessageDigest.getInstance("MD5");

byte[] buffer = new byte[2048];
int length = -1;
while ((length = fileStream.read(buffer)) != -1) {
md.update(buffer, 0, length);
}
byte[] b = md.digest();
return byteToHexString(b);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}finally{
try {
fileStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

private static String byteToHexString(byte[] tmp) {
String s;
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i]; // 取第 i 个字节
str[k++] = hexdigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
// >>> 为逻辑右移,将符号位一起右移
str[k++] = hexdigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
s = new String(str); // 换后的结果转换为字符串
return s;
}
}


7、上传文件到Azure Storage,使用MultipartFile数组接收多文件的上传,然后将接收到的文件upload到Azure Storage中存成blob


package com.xiaofangtech.sunt.storage;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.xiaofangtech.sunt.utils.MyUtils;
import com.xiaofangtech.sunt.utils.ResultMsg;
import com.xiaofangtech.sunt.utils.ResultStatusCode;

@RestController
@RequestMapping("file")
public class FileUploadModule {

@Autowired
private StorageConfig storageConfig;

//设置缩略图的宽高
private static int thumbnailWidth = 150;
private static int thumbnailHeight = 100;

@RequestMapping(value = "upload", method = RequestMethod.POST)
public Object uploadFile(String id, int type, @RequestPart("file") MultipartFile[] multipartFile)
{
List<BlobUpload> blobUploadEntities = new ArrayList<BlobUpload>();
try
{
if(multipartFile != null)
{

//获取或创建container
CloudBlobContainer blobContainer = BlobHelper.getBlobContainer(id.toLowerCase(), storageConfig);
for (int i=0;i<multipartFile.length;i++)
{
MultipartFile tempMultipartFile = multipartFile[i];
if (!tempMultipartFile.isEmpty())
{
try
{
//过滤非jpg,png格式的文件
if(!(tempMultipartFile.getContentType().toLowerCase().equals("image/jpg")
|| tempMultipartFile.getContentType().toLowerCase().equals("image/jpeg")
|| tempMultipartFile.getContentType().toLowerCase().equals("image/png")))
{
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.MEDIATYPE_NOTSUPPORT.getErrcode(),
ResultStatusCode.MEDIATYPE_NOTSUPPORT.getErrmsg(), null);
return resultMsg;
}
//拼装blob的名称(前缀名称+文件的md5值+文件扩展名称)
String checkSum = MyUtils.getMD5(tempMultipartFile.getInputStream());
String fileExtension = getFileExtension(tempMultipartFile.getOriginalFilename()).toLowerCase();
String preName = getBlobPreName(type, false).toLowerCase();
String blobName = preName + checkSum + fileExtension;

//设置文件类型,并且上传到azure blob
CloudBlockBlob blob = blobContainer.getBlockBlobReference(blobName);
blob.getProperties().setContentType(tempMultipartFile.getContentType());
blob.upload(tempMultipartFile.getInputStream(), tempMultipartFile.getSize());

//生成缩略图,并上传至AzureStorage
BufferedImage img = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB);
img.createGraphics().drawImage(ImageIO.read(tempMultipartFile.getInputStream()).getScaledInstance(thumbnailWidth, thumbnailHeight, Image.SCALE_SMOOTH),0,0,null);
ByteArrayOutputStream thumbnailStream = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", thumbnailStream);
InputStream inputStream = new ByteArrayInputStream(thumbnailStream.toByteArray());

String thumbnailPreName = getBlobPreName(type, true).toLowerCase();
String thumbnailCheckSum = MyUtils.getMD5(new ByteArrayInputStream(thumbnailStream.toByteArray()));
String blobThumbnail = thumbnailPreName + thumbnailCheckSum + ".jpg";
CloudBlockBlob thumbnailBlob = blobContainer.getBlockBlobReference(blobThumbnail);
thumbnailBlob.getProperties().setContentType("image/jpeg");
thumbnailBlob.upload(inputStream, thumbnailStream.toByteArray().length);

//将上传后的图片URL返回
BlobUpload blobUploadEntity = new BlobUpload();
blobUploadEntity.setFileName(tempMultipartFile.getOriginalFilename());
blobUploadEntity.setFileUrl(blob.getUri().toString());
blobUploadEntity.setThumbnailUrl(thumbnailBlob.getUri().toString());

blobUploadEntities.add(blobUploadEntity);
}
catch(Exception e)
{
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(),
ResultStatusCode.SYSTEM_ERR.getErrmsg(), null);
return resultMsg;
}
}
}
}
}
catch(Exception e)
{
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(),
ResultStatusCode.SYSTEM_ERR.getErrmsg(), null);
return resultMsg;
}

ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(),
ResultStatusCode.OK.getErrmsg(), blobUploadEntities);
return resultMsg;
}

private String getFileExtension(String fileName)
{
int position = fileName.indexOf('.');
if (position > 0)
{
String temp = fileName.substring(position);
return temp;
}
return "";
}

private String getBlobPreName(int fileType, Boolean thumbnail)
{
String afterName = "";
if (thumbnail)
{
afterName = "thumbnail/";
}

switch (fileType)
{
case 1:
return "logo/" + afterName;
case 2:
return "food/" + afterName;
case 3:
return "head/" + afterName;
case 4:
return "ads/" + afterName;
default :
return "";
}
}
}

8、测试

上传文件

Spring Boot实战之文件上传存入Azure StorageSpring Boot实战之文件上传存入Azure Storage

Spring Boot实战之文件上传存入Azure Storage

Spring Boot实战之文件上传存入Azure Storage


测试访问图片

Spring Boot实战之文件上传存入Azure StorageSpring Boot实战之文件上传存入Azure Storage

Spring Boot实战之文件上传存入Azure Storage

Spring Boot实战之文件上传存入Azure Storage