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

时间:2022-11-11 16:50:15

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