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; } }
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; } }
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、测试
上传文件
测试访问图片