SpringBoot 实现 Azure Blob 文件上传 postman 测试成功
背景准备:创建Azure Blob快速入门:使用 Azure 门户上传、下载和列出 Blob开发步骤配置本章与Azure的交互使用到Azure storage相关的依赖库,配置pom.xml,下载依赖库<dependency><groupId>com.microsoft.azure</groupId><artifactId>azure-storage
·
需求
使用 SpringBoot 实现 Azure Blob 文件上传 功能,并返回资源URL
准备:创建Azure Blob
开发步骤
配置
- 本章与Azure的交互使用到Azure storage相关的依赖库,配置pom.xml,下载依赖库
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>4.0.0</version>
</dependency>
- 配置属性文件 application.properties
azureblob.defaultEndpointsProtocol=https
azureblob.blobEndpoint=***
azureblob.queueEndpoint=***
azureblob.tableEndpoint=***
azureblob.accountName=***
azureblob.accountKey=***
代码编写
- 添加azure storage的配置信息类StorageConfig,用来从配置文件中读取配置连接信息
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "azureblob")
@NoArgsConstructor
@Data
public class StorageConfig {
@Value("${azureblob.defaultEndpointsProtocol}")
private String defaultEndpointsProtocol;
@Value("${azureblob.blobEndpoint}")
private String blobEndpoint;
@Value("${azureblob.queueEndpoint}")
private String queueEndpoint;
@Value("${azureblob.tableEndpoint}")
private String tableEndpoint;
@Value("${azureblob.accountName}")
private String accountName;
@Value("${azureblob.accountKey}")
private String accountKey;
}
- 新建帮助类,BlobHelper.java 用来获取或创建Blob所在容器
package com.**.Helper;
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;
import com.**.Base.StorageConfig;
/**
* @author lzz
* @version 1.0
* @description: TODO
* @date 2020/12/21 17:27
*/
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;
}
}
}
- 定义上传成功后,返回的结果类,包含上传到azure storage后文件的链接,缩略图链接
package com.**.Entity;
/**
* @author lzz
* @version 1.0
* @description: TODO
* @date 2020/12/21 17:30
*/
public class BlobUpload {
private long fileid;
private String filetype;
private String typeid;
private String filename;
private String fileurl;
private String thumbnailurl;
public BlobUpload() {
}
public BlobUpload(long file_id, String file_type, String type_id, String file_name, String file_url, String thumbnail_url) {
this.fileid = file_id;
this.filetype = file_type;
this.typeid = type_id;
this.filename = file_name;
this.fileurl = file_url;
this.thumbnailurl = thumbnail_url;
}
public long getFile_id() {
return fileid;
}
public void setFile_id(long file_id) {
this.fileid = file_id;
}
public String getFilename() {
return filename;
}
public void setFileName(String file_name) {
this.filename = file_name;
}
public String getFileUrl() {
return fileurl;
}
public void setFileUrl(String file_url) {
this.fileurl = file_url;
}
public String getThumbnail_url() {
return thumbnailurl;
}
public void setThumbnail_url(String thumbnailurl) {
this.thumbnailurl = thumbnailurl;
}
public String getFile_type() {
return filetype;
}
public void setFile_type(String file_type) {
this.filetype = file_type;
}
public String getType_id() {
return typeid;
}
public void setType_id(String type_id) {
this.typeid = type_id;
}
@Override
public String toString() {
return "BlobUpload{" +
"fileid=" + fileid +
", filetype='" + filetype + '\'' +
", typeid='" + typeid + '\'' +
", filename='" + filename + '\'' +
", fileurl='" + fileurl + '\'' +
", thumbnailurl='" + thumbnailurl + '\'' +
'}';
}
}
- 修改MyUtil.java 添加获取文件MD5值的方法
package com.**.Util;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
/**
* @author lzz
* @version 1.0
* @description: TODO
* @date 2020/12/21 17:32
*/
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;
}
}
- 定义Spring Data 用于数据访问
package com.**.Repository;
import com.**.Entity.BlobUpload;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface FileUploadRepository extends CrudRepository<BlobUpload, String> {
@Query(value ="SELECT * FROM FileUpload fu WHERE fu.fileType=:type and fu.typeId=:typeId")
BlobUpload GetFileByFileTypeAndId(@Param("fileType")String fileType, @Param("typeId")long typeId);
@Query(value ="SELECT * FROM FileUpload fu WHERE fu.fileId=:fileId")
BlobUpload GetFileByFileId(@Param("fileId")long fileId);
@Query(value ="SELECT * FROM FileUpload")
List<BlobUpload> GetFiles();
@Transactional
@Modifying
@Query(value ="INSERT INTO FileUpload values(:fileType,:typeId,:fileName,:fileUrl,:thumbnailUrl)")
int AddFileUpload(@Param("fileType")String fileType,@Param("typeId")long typeId,@Param("fileName")String fileName,
@Param("fileUrl")String fileUrl,@Param("thumbnailUrl")String thumbnailUrl);
@Transactional
@Modifying
@Query(value ="UPDATE FileUpload " +
"SET fileName = :newName ,fileUrl = :newFileUrl ,thumbnailUrl = :newThumbnailUrl"+
"WHERE fileType = :fileType and typeId=:typeId")
int UpdateFileUpload(@Param("typeId") long typeId,@Param("newName")String newFileName,
@Param("newFileUrl")String newFileUrl,@Param("newThumbnailUrl")String newThumbnailUrl,
@Param("fileType")String fileType);
@Transactional
@Modifying
@Query(value ="DELETE FROM FileUpload WHERE FileId = :id")
int DeleteFileUpload(long id);
}
- 上传文件到Azure Storage,使用MultipartFile数组接收多文件的上传,然后将接收到的文件upload到Azure Storage中存成blob
package com.**.Controller;
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 com.**.Base.Result;
import com.**.Base.StorageConfig;
import com.**.Entity.BlobUpload;
import com.**.Helper.BlobHelper;
import com.**.Repository.FileUploadRepository;
import com.**.Util.MyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
/**
* @author lzz
* @version 1.0
* @description: TODO
* @date 2020/12/21 17:35
*/
@RestController
@RequestMapping("/fileUpload")
@CrossOrigin(origins = "*")
public class FileUploadController {
@Autowired
private StorageConfig storageConfig;
private final FileUploadRepository fileUploadRepository;
public FileUploadController(FileUploadRepository fileUploadRepository) {
this.fileUploadRepository = fileUploadRepository;
}
//设置缩略图的宽高
private static int thumbnailWidth = 150;
private static int thumbnailHeight = 100;
// @RequestMapping(value = "upload", method = RequestMethod.POST)
@PostMapping("/{type}/{id}")
public Object uploadFile(@PathVariable(value = "type")int type,@PathVariable(value = "id")long typeId, @RequestPart("file") MultipartFile[] multipartFile)
{
List<BlobUpload> blobUploadEntities = new ArrayList<BlobUpload>();
List<String> dataUrl = new ArrayList<String>();
// System.out.println(type);
// System.out.println(typeId);
try
{
if(multipartFile != null)
{
//获取或创建container
CloudBlobContainer blobContainer = BlobHelper.getBlobContainer("mycontain", 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")))
{
return new Result().fail("the format of file is incorrect",500);
}
//拼装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);
// System.out.println(thumbnailBlob.getUri());
dataUrl.add(thumbnailBlob.getUri().toString());
//将上传后的图片URL返回
BlobUpload blobUploadEntity = new BlobUpload();
blobUploadEntity.setFileName(tempMultipartFile.getOriginalFilename());
blobUploadEntity.setFileUrl(blob.getUri().toString());
blobUploadEntities.add(blobUploadEntity);
// System.out.println(blob.getUri().toString());
dataUrl.add(blob.getUri().toString());
String FileName=preName.substring(0,preName.length()-1);
System.out.println(FileName);
System.out.println(typeId);
System.out.println(checkSum + fileExtension);
System.out.println(blob.getUri().toString());
System.out.println(thumbnailBlob.getUri().toString());
int row=fileUploadRepository.AddFileUpload(FileName, typeId, checkSum + fileExtension, blob.getUri().toString(), thumbnailBlob.getUri().toString());
if(row!=1){
return new Result().fail("upload file failed 1",500);
}
}
catch(Exception e)
{
return new Result().fail("upload file failed 2",500);
}
}
}
}
}
catch(Exception e)
{
return new Result().fail("upload file failed 3",500);
}
return new Result().success(dataUrl);
}
@DeleteMapping("/{id}")
public Result deleteShowcase(@PathVariable("id")long fileId){
System.out.println(fileId);
BlobUpload blobUpload = fileUploadRepository.GetFileByFileId(fileId);
if(blobUpload==null){
return new Result().fail("delete file fail,can not find file by id",500);
}
int row=fileUploadRepository.DeleteFileUpload(fileId);
if(row!=0){
List<BlobUpload> files= fileUploadRepository.GetFiles();
return new Result().success(files);
}else{
return new Result().fail("delete file fail",500);
}
}
private String getFileExtension(String fileName)
{
int position = fileName.indexOf('.');
if (position > 0)
{
return fileName.substring(position);
}
return "";
}
private String getBlobPreName(int fileType, Boolean thumbnail)
{
String afterName = "";
if (thumbnail)
{
afterName = "thumbnail/";
}
switch (fileType)
{
case 1:
return "11/" + afterName;
case 2:
return "22/" + afterName;
case 3:
return "33/" + afterName;
case 4:
return "44/" + afterName;
case 5:
return "55/" + afterName;
default :
return "";
}
}
}
代码中的结果集 Result 定义如下:
package com.**.Base;
import lombok.Getter;
/**
1. 返回结果实体类
2. 3. @author lzz
*/
@Getter
public class Result {
private int code;
private String message;
private Object data;
private Result setResult(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
return this;
}
public Result success() {
return setResult(200, "Success", null);
}
public Result success(Object data) {
return setResult(200, "Success", data);
}
public Result fail(Object data, String message) {
return setResult(400, message, data);
}
public Result fail(Object data, String message, int code) {
return setResult(code, message, data);
}
public Result fail(String message, int code) {
return setResult(code, message, null);
}
}
Postman测试
【请求体】-【form-data】-【key=file】,然后从本地选择图片
返回结果中包含存储的图片链接和缩略图链接
打开两个链接进行测试
缩略图链接测试正常:
原始图片链接测试正常
参考文献
更多推荐
所有评论(0)