SpringBoot 实现 Azure Blob 文件上传 postman 测试成功

需求

使用 SpringBoot 实现 Azure Blob 文件上传 功能,并返回资源URL

准备:创建Azure Blob

快速入门:使用 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】,然后从本地选择图片
返回结果中包含存储的图片链接和缩略图链接
在这里插入图片描述

打开两个链接进行测试
缩略图链接测试正常:
在这里插入图片描述
原始图片链接测试正常
在这里插入图片描述

参考文献

  1. Spring Boot实战之文件上传存入Azure Storage
  2. SpringBoot实现azure blob的文件上传
  3. 用于 Java 的 Azure 存储库
  4. 快速入门:使用 Azure 门户上传、下载和列出 Blob
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值