SpringBoot集成阿里云OSS、华为云OBS、七牛云、又拍云等上传案例【附白嫖方案】【附源码】

1. 项目背景

唉!本文写起来都是泪点。不是刻意写的本文,主要是对日常用到的文件上传做了一个汇总总结,同时希望可以给用到的小伙伴带来一点帮助吧。

  • 上传本地,这个就不水了,基本做技术的都用到过吧;

  • 阿里云OSS,阿里云是业界巨鳄了吧,用到的人肯定不少吧,不过博主好久不用了,简单记录下;

  • 华为云OBS,工作需要,也简单记录下吧;

  • 七牛云,个人网站最开始使用的图床,目的是为了白嫖10G文件存储。后来网站了升级了https域名,七牛云免费只支持http,https域名加速是收费的。https域名的网站在谷歌上请求图片时会强制升级为https。

  • 又拍云,个人网站目前在用的图床,加入了又拍云联盟,网站底部挂链接,算是推广合作模式吧(对我这种不介意的来说就是白嫖)。速度还行,可以去我的网站看一下:笑小枫

还有腾讯云等等云,暂没用过,就先不整理了,使用都很简单,SDK文档很全,也很简单。

2. 上传思路

分为两点来说。本文的精华也都在这里了,统一思想。

2.1 前端调用上传文件

前端上传的话,应该是我们常用的吧,通过@RequestParam(value = "file") MultipartFile file接收,然后转为InputStream or byte[] or File,然后调用上传就可以了,核心也就在这,很简单的,尤其上传到云服务器,装载好配置后,直接调用SDK接口即可。

2.2 通过url地址上传网络文件

通过url上传应该很少用到吧,使用场景呢,例如爬取文章的时候,把网络图片上传到自己的图床;图片库根据url地址迁移。

说到这,突然想起了一个问题,大家写文章的时候,图片上传到图床后在文章内是怎么保存的呢?是全路径还是怎么保存的?如果加速域名换了,或者换图床地址了,需要怎么迁移。希望有经验的大佬可以留言指导!

3. 上传到本地

这个比较简单啦,贴下核心代码吧

  • 在yml配置下上传路径
file:local:maxFileSize: 10485760imageFilePath: D:/test/image/docFilePath: D:/test/file/
  • 创建配置类,读取配置文件的参数
package com.maple.upload.properties;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** 上传本地配置** @author 笑小枫* @date 2022/7/22* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>*/
@Data
@Configuration
public class LocalFileProperties {// ---------------本地文件配置 start------------------/*** 图片存储路径*/@Value("${file.local.imageFilePath}")private String imageFilePath;/*** 文档存储路径*/@Value("${file.local.docFilePath}")private String docFilePath;/*** 文件限制大小*/@Value("${file.local.maxFileSize}")private long maxFileSize;// --------------本地文件配置 end-------------------}
  • 创建上传下载工具类
package com.maple.upload.util;import com.maple.upload.properties.LocalFileProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;/*** @author 笑小枫* @date 2024/1/10* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>*/
@Slf4j
@Component
@AllArgsConstructor
public class LocalFileUtil {private final LocalFileProperties fileProperties;private static final List<String> FILE_TYPE_LIST_IMAGE = Arrays.asList("image/png","image/jpg","image/jpeg","image/bmp");/*** 上传图片*/public String uploadImage(MultipartFile file) {// 检查图片类型String contentType = file.getContentType();if (!FILE_TYPE_LIST_IMAGE.contains(contentType)) {throw new RuntimeException("上传失败,不允许的文件类型");}int size = (int) file.getSize();if (size > fileProperties.getMaxFileSize()) {throw new RuntimeException("文件过大");}String fileName = file.getOriginalFilename();//获取文件后缀String afterName = StringUtils.substringAfterLast(fileName, ".");//获取文件前缀String prefName = StringUtils.substringBeforeLast(fileName, ".");//获取一个时间毫秒值作为文件名fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "_" + prefName + "." + afterName;File filePath = new File(fileProperties.getImageFilePath(), fileName);//判断文件是否已经存在if (filePath.exists()) {throw new RuntimeException("文件已经存在");}//判断文件父目录是否存在if (!filePath.getParentFile().exists()) {filePath.getParentFile().mkdirs();}try {file.transferTo(filePath);} catch (IOException e) {log.error("图片上传失败", e);throw new RuntimeException("图片上传失败");}return fileName;}/*** 批量上传文件*/public List<Map<String, Object>> uploadFiles(MultipartFile[] files) {int size = 0;for (MultipartFile file : files) {size = (int) file.getSize() + size;}if (size > fileProperties.getMaxFileSize()) {throw new RuntimeException("文件过大");}List<Map<String, Object>> fileInfoList = new ArrayList<>();for (int i = 0; i < files.length; i++) {Map<String, Object> map = new HashMap<>();String fileName = files[i].getOriginalFilename();//获取文件后缀String afterName = StringUtils.substringAfterLast(fileName, ".");//获取文件前缀String prefName = StringUtils.substringBeforeLast(fileName, ".");String fileServiceName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + i + "_" + prefName + "." + afterName;File filePath = new File(fileProperties.getDocFilePath(), fileServiceName);// 判断文件父目录是否存在if (!filePath.getParentFile().exists()) {filePath.getParentFile().mkdirs();}try {files[i].transferTo(filePath);} catch (IOException e) {log.error("文件上传失败", e);throw new RuntimeException("文件上传失败");}map.put("fileName", fileName);map.put("filePath", filePath);map.put("fileServiceName", fileServiceName);fileInfoList.add(map);}return fileInfoList;}/*** 批量删除文件** @param fileNameArr 服务端保存的文件的名数组*/public void deleteFile(String[] fileNameArr) {for (String fileName : fileNameArr) {String filePath = fileProperties.getDocFilePath() + fileName;File file = new File(filePath);if (file.exists()) {try {Files.delete(file.toPath());} catch (IOException e) {e.printStackTrace();log.warn("文件删除失败", e);}} else {log.warn("文件: {} 删除失败,该文件不存在", fileName);}}}/*** 下载文件*/public void downLoadFile(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {String encodeFileName = URLDecoder.decode(fileName, "UTF-8");File file = new File(fileProperties.getDocFilePath() + encodeFileName);// 下载文件if (!file.exists()) {throw new RuntimeException("文件不存在!");}try (FileInputStream inputStream = new FileInputStream(file);ServletOutputStream outputStream = response.getOutputStream()) {response.reset();//设置响应类型	PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。response.setContentType("application/octet-stream;charset=utf-8");//设置响应的文件名称,并转换成中文编码String afterName = StringUtils.substringAfterLast(fileName, "_");//保存的文件名,必须和页面编码一致,否则乱码afterName = response.encodeURL(new String(afterName.getBytes(), StandardCharsets.ISO_8859_1.displayName()));response.setHeader("Content-type", "application-download");//attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果response.addHeader("Content-Disposition", "attachment;filename=" + afterName);response.addHeader("filename", afterName);//将文件读入响应流int length = 1024;byte[] buf = new byte[1024];int readLength = inputStream.read(buf, 0, length);while (readLength != -1) {outputStream.write(buf, 0, readLength);readLength = inputStream.read(buf, 0, length);}outputStream.flush();} catch (Exception e) {e.printStackTrace();}}
}

访问图片的话,可以通过重写WebMvcConfigureraddResourceHandlers方法来实现。

通过请求/local/images/**将链接虚拟映射到我们配置的localFileProperties.getImageFilePath()下,文件访问同理。

详细代码如下

package com.maple.upload.config;import com.maple.upload.properties.LocalFileProperties;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/10*/
@Configuration
@AllArgsConstructor
public class LocalFileConfig implements WebMvcConfigurer {private final LocalFileProperties localFileProperties;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {// 重写方法// 修改tomcat 虚拟映射// 定义图片存放路径registry.addResourceHandler("/local/images/**").addResourceLocations("file:" + localFileProperties.getImageFilePath());//定义文档存放路径registry.addResourceHandler("/local/doc/**").addResourceLocations("file:" + localFileProperties.getDocFilePath());}
}
  • controller调用代码
package com.maple.upload.controller;import com.maple.upload.util.LocalFileUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;/*** 文件相关操作接口** @author 笑小枫* @date 2024/1/10* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>*/
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/local")
public class LocalFileController {private final LocalFileUtil fileUtil;/*** 图片上传*/@PostMapping("/uploadImage")public String uploadImage(@RequestParam(value = "file") MultipartFile file) {if (file.isEmpty()) {throw new RuntimeException("图片内容为空,上传失败!");}return fileUtil.uploadImage(file);}/*** 文件批量上传*/@PostMapping("/uploadFiles")public List<Map<String, Object>> uploadFiles(@RequestParam(value = "file") MultipartFile[] files) {return fileUtil.uploadFiles(files);}/*** 批量删除文件*/@PostMapping("/deleteFiles")public void deleteFiles(@RequestParam(value = "files") String[] files) {fileUtil.deleteFile(files);}/*** 文件下载功能*/@GetMapping(value = "/download/{fileName:.*}")public void download(@PathVariable("fileName") String fileName, HttpServletResponse response) {try {fileUtil.downLoadFile(response, fileName);} catch (Exception e) {log.error("文件下载失败", e);}}
}

调用上传图片的接口,可以看到图片已经上传成功。

image-20240116102717345

通过请求/local/images/**将链接虚拟映射我们图片上。

image-20240116103037587

批量上传,删除等操作就不一一演示截图了,代码已贴,因为是先写的demo,后写的文章,获取代码片贴的有遗漏,如有遗漏,可以去文章底部查看源码地址。

4. 上传阿里云OSS

阿里云OSS官方sdk使用文档:https://help.aliyun.com/zh/oss/developer-reference/java

阿里云OSS操作指南:https://help.aliyun.com/zh/oss/user-guide

公共云下OSS Region和Endpoint对照表:https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints

更多公共云下OSS Region和Endpoint对照,参考上面链接

image-20240116103832968

  • 引入oss sdk依赖
        <!-- 阿里云OSS --><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.8.1</version></dependency>
  • 配置上传配置信息
file:oss:bucketName: mapleBucketaccessKeyId: your aksecretAccessKey: your skendpoint: oss-cn-shanghai.aliyuncs.comshowUrl: cdn地址-file.xiaoxiaofeng.com
  • 创建配置类,读取配置文件的参数
/** Copyright (c) 2018-2999 上海合齐软件科技科技有限公司 All rights reserved.**** 未经允许,不可做商业用途!** 版权所有,侵权必究!*/package com.maple.upload.properties;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** 阿里云OSS配置** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/10*/
@Data
@Configuration
public class AliOssProperties {@Value("${file.oss.bucketName}")private String bucketName;@Value("${file.oss.accessKeyId}")private String accessKeyId;@Value("${file.oss.secretAccessKey}")private String secretAccessKey;@Value("${file.oss.endpoint}")private String endpoint;@Value("${file.oss.showUrl}")private String showUrl;
}
  • 上传工具类
package com.maple.upload.util;import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectResult;
import com.maple.upload.properties.AliOssProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;/*** 阿里云OSS 对象存储工具类* 阿里云OSS官方sdk使用文档:https://help.aliyun.com/zh/oss/developer-reference/java* 阿里云OSS操作指南:https://help.aliyun.com/zh/oss/user-guide* * @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/15*/
@Slf4j
@Component
@AllArgsConstructor
public class AliOssUtil {private final AliOssProperties aliOssProperties;public String uploadFile(MultipartFile file) {String fileName = file.getOriginalFilename();if (StringUtils.isBlank(fileName)) {throw new RuntimeException("获取文件信息失败");}// 组建上传的文件名称,命名规则可自定义更改String objectKey = FileCommonUtil.setFilePath("xiaoxiaofeng") + FileCommonUtil.setFileName("xxf", fileName.substring(fileName.lastIndexOf(".")));//构造一个OSS对象的配置类OSS ossClient = new OSSClientBuilder().build(aliOssProperties.getEndpoint(), aliOssProperties.getAccessKeyId(), aliOssProperties.getSecretAccessKey());try (InputStream inputStream = file.getInputStream()) {log.info(String.format("阿里云OSS上传开始,原文件名:%s,上传后的文件名:%s", fileName, objectKey));PutObjectResult result = ossClient.putObject(aliOssProperties.getBucketName(), objectKey, inputStream);log.info(String.format("阿里云OSS上传结束,文件名:%s,返回结果:%s", objectKey, result.toString()));return aliOssProperties.getShowUrl() + objectKey;} catch (Exception e) {log.error("调用阿里云OSS失败", e);throw new RuntimeException("调用阿里云OSS失败");}}
}

因为篇幅问题,controller就不贴了。暂时没有阿里云oss的资源了,这里就不做测试,小伙伴在使用过程中如有问题,麻烦留言告诉我下!

5. 上传华为云OBS

华为云OBS官方sdk使用文档:https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0101.html

华为云OBS操作指南:https://support.huaweicloud.com/ugobs-obs/obs_41_0002.html

华为云各服务应用区域和各服务的终端节点:https://developer.huaweicloud.com/endpoint?OBS

  • 引入sdk依赖
        <!-- 华为云OBS --><dependency><groupId>com.huaweicloud</groupId><artifactId>esdk-obs-java-bundle</artifactId><version>3.21.8</version></dependency>
  • 配置上传配置信息
file:obs:bucketName: mapleBucketaccessKey: your aksecretKey: your skendPoint: obs.cn-east-2.myhuaweicloud.comshowUrl: cdn地址-file.xiaoxiaofeng.com
  • 创建配置类,读取配置文件的参数
package com.maple.upload.properties;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** 华为云上传配置** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/10*/
@Data
@Configuration
public class HwyObsProperties {@Value("${file.obs.bucketName}")private String bucketName;@Value("${file.obs.accessKey}")private String accessKey;@Value("${file.obs.secretKey}")private String secretKey;@Value("${file.obs.endPoint}")private String endPoint;@Value("${file.obs.showUrl}")private String showUrl;
}
  • 上传工具类
package com.maple.upload.util;import com.alibaba.fastjson.JSON;
import com.maple.upload.bean.HwyObsModel;
import com.maple.upload.properties.HwyObsProperties;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.PostSignatureRequest;
import com.obs.services.model.PostSignatureResponse;
import com.obs.services.model.PutObjectResult;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;/*** 华为云OBS 对象存储工具类* 华为云OBS官方sdk使用文档:https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0101.html* 华为云OBS操作指南:https://support.huaweicloud.com/ugobs-obs/obs_41_0002.html* 华为云各服务应用区域和各服务的终端节点:https://developer.huaweicloud.com/endpoint?OBS** @author 笑小枫* @date 2022/7/22* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>*/
@Slf4j
@Component
@AllArgsConstructor
public class HwyObsUtil {private final HwyObsProperties fileProperties;/*** 上传华为云obs文件存储** @param file 文件* @return 文件访问路径, 如果配置CDN,这里直接返回CDN+文件名(objectKey)*/public String uploadFileToObs(MultipartFile file) {String fileName = file.getOriginalFilename();if (StringUtils.isBlank(fileName)) {throw new RuntimeException("获取文件信息失败");}// 文件类型String fileType = fileName.substring(file.getOriginalFilename().lastIndexOf("."));// 组建上传的文件名称,命名规则可自定义更改String objectKey = FileCommonUtil.setFilePath("") + FileCommonUtil.setFileName(null, fileType);PutObjectResult putObjectResult;try (InputStream inputStream = file.getInputStream();ObsClient obsClient = new ObsClient(fileProperties.getAccessKey(), fileProperties.getSecretKey(), fileProperties.getEndPoint())) {log.info(String.format("华为云obs上传开始,原文件名:%s,上传后的文件名:%s", fileName, objectKey));putObjectResult = obsClient.putObject(fileProperties.getBucketName(), objectKey, inputStream);log.info(String.format("华为云obs上传结束,文件名:%s,返回结果:%s", objectKey, JSON.toJSONString(putObjectResult)));} catch (ObsException | IOException e) {log.error("华为云obs上传文件失败", e);throw new RuntimeException("华为云obs上传文件失败,请重试");}if (putObjectResult.getStatusCode() == 200) {return putObjectResult.getObjectUrl();} else {throw new RuntimeException("华为云obs上传文件失败,请重试");}}/*** 获取华为云上传token,将token返回给前端,然后由前端上传,这样文件不占服务器端带宽** @param fileName 文件名称* @return*/public HwyObsModel getObsConfig(String fileName) {if (StringUtils.isBlank(fileName)) {throw new RuntimeException("The fileName cannot be empty.");}String obsToken;String objectKey = null;try (ObsClient obsClient = new ObsClient(fileProperties.getAccessKey(), fileProperties.getSecretKey(), fileProperties.getEndPoint())) {String fileType = fileName.substring(fileName.lastIndexOf("."));// 组建上传的文件名称,命名规则可自定义更改objectKey = FileCommonUtil.setFilePath("") + FileCommonUtil.setFileName("", fileType);PostSignatureRequest request = new PostSignatureRequest();// 设置表单上传请求有效期,单位:秒request.setExpires(3600);request.setBucketName(fileProperties.getBucketName());if (StringUtils.isNotBlank(objectKey)) {request.setObjectKey(objectKey);}PostSignatureResponse response = obsClient.createPostSignature(request);obsToken = response.getToken();} catch (ObsException | IOException e) {log.error("华为云obs上传文件失败", e);throw new RuntimeException("华为云obs上传文件失败,请重试");}HwyObsModel obsModel = new HwyObsModel();obsModel.setBucketName(fileProperties.getBucketName());obsModel.setEndPoint(fileProperties.getEndPoint());obsModel.setToken(obsToken);obsModel.setObjectKey(objectKey);obsModel.setShowUrl(fileProperties.getShowUrl());return obsModel;}
}

篇幅问题,不贴controller和测试截图了,完整代码参考文章底部源码吧,思想明白了,别的都大差不差。

6. 上传七牛云

七牛云官方sdk:https://developer.qiniu.com/kodo/1239/java

七牛云存储区域表链接:https://developer.qiniu.com/kodo/1671/region-endpoint-fq

  • 引入sdk依赖
        <!-- 七牛云 --><dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>7.2.29</version></dependency>
  • 配置上传配置信息
file:qiniuyun:bucket: mapleBucketaccessKey: your aksecretKey: your skregionId: z1showUrl: cdn地址-file.xiaoxiaofeng.com
  • 创建配置类,读取配置文件的参数
package com.maple.upload.properties;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** 七牛云配置** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/10*/
@Data
@Configuration
public class QiNiuProperties {@Value("${file.qiniuyun.accessKey}")private String accessKey;@Value("${file.qiniuyun.secretKey}")private String secretKey;@Value("${file.qiniuyun.bucket}")private String bucket;@Value("${file.qiniuyun.regionId}")private String regionId;@Value("${file.qiniuyun.showUrl}")private String showUrl;
}
  • 上传工具类,注意有一个区域转换,如后续有新增,qiNiuConfig这里需要调整一下
package com.maple.upload.util;import com.maple.upload.properties.QiNiuProperties;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
import java.util.Objects;/*** * 七牛云 对象存储工具类* 七牛云官方sdk:https://developer.qiniu.com/kodo/1239/java* 七牛云存储区域表链接:https://developer.qiniu.com/kodo/1671/region-endpoint-fq* * @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2022/3/24*/
@Slf4j
@Component
@AllArgsConstructor
public class QiNiuYunUtil {private final QiNiuProperties qiNiuProperties;public String uploadFile(MultipartFile file) {String fileName = file.getOriginalFilename();if (StringUtils.isBlank(fileName)) {throw new RuntimeException("获取文件信息失败");}// 组建上传的文件名称,命名规则可自定义更改String objectKey = FileCommonUtil.setFilePath("xiaoxiaofeng") + FileCommonUtil.setFileName("xxf", fileName.substring(fileName.lastIndexOf(".")));//构造一个带指定 Region 对象的配置类Configuration cfg = qiNiuConfig(qiNiuProperties.getRegionId());//...其他参数参考类注释UploadManager uploadManager = new UploadManager(cfg);try (InputStream inputStream = file.getInputStream()) {Auth auth = Auth.create(qiNiuProperties.getAccessKey(), qiNiuProperties.getSecretKey());String upToken = auth.uploadToken(qiNiuProperties.getBucket());log.info(String.format("七牛云上传开始,原文件名:%s,上传后的文件名:%s", fileName, objectKey));Response response = uploadManager.put(inputStream, objectKey, upToken, null, null);log.info(String.format("七牛云上传结束,文件名:%s,返回结果:%s", objectKey, response.toString()));return qiNiuProperties.getShowUrl() + objectKey;} catch (Exception e) {log.error("调用七牛云失败", e);throw new RuntimeException("调用七牛云失败");}}private static Configuration qiNiuConfig(String zone) {Region region = null;if (Objects.equals(zone, "z1")) {region = Region.huabei();} else if (Objects.equals(zone, "z0")) {region = Region.huadong();} else if (Objects.equals(zone, "z2")) {region = Region.huanan();} else if (Objects.equals(zone, "na0")) {region = Region.beimei();} else if (Objects.equals(zone, "as0")) {region = Region.xinjiapo();}return new Configuration(region);}
}

篇幅问题,不贴controller和测试截图了,完整代码参考文章底部源码吧,思想明白了,别的都大差不差。

7. 上传又拍云

又拍云客户端配置:https://help.upyun.com/knowledge-base/quick_start/

又拍云官方sdk:https://github.com/upyun/java-sdk

  • 引入sdk依赖
        <!-- 又拍云OSS --><dependency><groupId>com.upyun</groupId><artifactId>java-sdk</artifactId><version>4.2.3</version></dependency>
  • 配置上传配置信息
file:upy:bucketName: mapleBucketuserName: 操作用户名称password: 操作用户密码showUrl: cdn地址-file.xiaoxiaofeng.com
  • 创建配置类,读取配置文件的参数
package com.maple.upload.properties;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** 又拍云上传配置** @author 笑小枫 <https://www.xiaoxiaofeng.com/>* @date 2024/1/10*/
@Data
@Configuration
public class UpyOssProperties {@Value("${file.upy.bucketName}")private String bucketName;@Value("${file.upy.userName}")private String userName;@Value("${file.upy.password}")private String password;/*** 加速域名*/@Value("${file.upy.showUrl}")private String showUrl;
}
  • 上传工具类
package com.maple.upload.util;import com.maple.upload.properties.UpyOssProperties;
import com.upyun.RestManager;
import com.upyun.UpException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.net.URI;/*** 又拍云 对象存储工具类* 又拍云客户端配置:https://help.upyun.com/knowledge-base/quick_start/* 又拍云官方sdk:https://github.com/upyun/java-sdk** @author 笑小枫* @date 2022/7/22* @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>*/
@Slf4j
@Component
@AllArgsConstructor
public class UpyOssUtil {private final UpyOssProperties fileProperties;/*** 根据url上传文件到又拍云*/public String uploadUpy(String url) {// 组建上传的文件名称,命名规则可自定义更改String fileName = FileCommonUtil.setFilePath("xiaoxiaofeng") + FileCommonUtil.setFileName("xxf", url.substring(url.lastIndexOf(".")));RestManager restManager = new RestManager(fileProperties.getBucketName(), fileProperties.getUserName(), fileProperties.getPassword());URI u = URI.create(url);try (InputStream inputStream = u.toURL().openStream()) {byte[] bytes = IOUtils.toByteArray(inputStream);Response response = restManager.writeFile(fileName, bytes, null);if (response.isSuccessful()) {return fileProperties.getShowUrl() + fileName;}} catch (IOException | UpException e) {log.error("又拍云oss上传文件失败", e);}throw new RuntimeException("又拍云oss上传文件失败,请重试");}/*** MultipartFile上传文件到又拍云*/public String uploadUpy(MultipartFile file) {String fileName = file.getOriginalFilename();if (StringUtils.isBlank(fileName)) {throw new RuntimeException("获取文件信息失败");}// 组建上传的文件名称,命名规则可自定义更改String objectKey = FileCommonUtil.setFilePath("xiaoxiaofeng") + FileCommonUtil.setFileName("xxf", fileName.substring(fileName.lastIndexOf(".")));RestManager restManager = new RestManager(fileProperties.getBucketName(), fileProperties.getUserName(), fileProperties.getPassword());try (InputStream inputStream = file.getInputStream()) {log.info(String.format("又拍云上传开始,原文件名:%s,上传后的文件名:%s", fileName, objectKey));Response response = restManager.writeFile(objectKey, inputStream, null);log.info(String.format("又拍云上传结束,文件名:%s,返回结果:%s", objectKey, response.isSuccessful()));if (response.isSuccessful()) {return fileProperties.getShowUrl() + objectKey;}} catch (IOException | UpException e) {log.error("又拍云oss上传文件失败", e);}throw new RuntimeException("又拍云oss上传文件失败,请重试");}
}

篇幅问题,不贴controller和测试截图了,完整代码参考文章底部源码吧,思想明白了,别的都大差不差。

8. 项目源码

本文到此就结束了,如果帮助到你了,帮忙点个赞👍

本文源码:https://github.com/hack-feng/maple-product/tree/main/maple-file-upload

🐾我是笑小枫,全网皆可搜的【笑小枫】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/691723.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

osqp-eigen学习

OSQP文档学习 参考博客&#xff1a; &#xff08;1&#xff09;二次规划&#xff08;QP&#xff09;与OSQP求解器 &#xff08;2&#xff09;如何使用OSQP-Eigen osqp-eigen 1 osqp-eigen接口 以下列问题的求解为例&#xff1a; s.t. 1 ≤ x 1 ≤ 1.5 1≤x_1≤1.5 1≤x1​≤…

毕业设计:基于知识图谱的《红楼梦》人物关系可视化

文章目录 项目介绍部署步骤项目运行 项目介绍 github地址&#xff1a;https://github.com/chizhu/KGQA_HLM?tabreadme-ov-file 基于知识图谱的《红楼梦》人物关系可视化&#xff1a;应该是重庆邮电大学林智敏同学的毕业设计&#xff0c;在学习知识图谱的过程中参考使用。 文…

专修戴尔R730xd服务器闪电灯 心跳亮黄灯故障

2024年开年第二天接到一个用户反馈说他公司有一台DELL PowerEdge R730xd服务器春节前由于市电问题意外断电关机了&#xff0c;刚好碰上春节就没去开机了&#xff0c;今天工厂开工服务器通电发现开不了机&#xff0c;且机器过了一会后报了2个黄灯错误&#xff0c;如下图&#xf…

proton.me邮箱

https://proton.me/ 我开发的chatgpt网站&#xff1a; https://chat.xutongbao.top/

软件代码写作要点

代码写得好不好,完全在于规划的好不好,那么流程图就显得十分的重要. 虽然大神都不用写流程图,对于复杂的流程,事前规划好流程图是必不可少的秘密. 流程图可以清晰的显示逻辑关系,以及逻辑是否有漏洞,简单清晰的流程图可以把复杂问题简单化,更有助于检测错误缺陷,对于代码技能…

重铸安卓荣光——上传图片组件

痛点&#xff1a; 公司打算做安卓软件&#xff0c;最近在研究安卓&#xff0c;打算先绘制样式 研究发现安卓并不像前端有那么多组件库&#xff0c;甚至有些基础的组件都需要自己实现&#xff0c;记录一下自己实现的组件 成品展示 一个上传图片的组件 可以选择拍照或者从相册中…

75.SpringMVC的拦截器和过滤器有什么区别?执行顺序?

75.SpringMVC的拦截器和过滤器有什么区别&#xff1f;执行顺序&#xff1f; 区别 拦截器不依赖与servlet容器&#xff0c;过滤器依赖与servlet容器。拦截器只能对action请求(DispatcherServlet 映射的请求)起作用&#xff0c;而过滤器则可以对几乎所有的请求起作用。拦截器可…

压缩感知——革新数据采集的科学魔法

引言&#xff1a; 在数字时代&#xff0c;数据以及数据的收集和处理无处不在。压缩感知(Compressed Sensing, CS)是一种新兴的数学框架&#xff0c;它挑战了我们传统上对数据采集和压缩的看法&#xff0c;给医学图像、天文观测、环境监测等领域带来了颠覆性的影响。但到底什么…

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

Leetcode日记 226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 二叉树简介二叉树分类解题方法制作不易&#xff0c;感谢三连&#xff0c;谢谢啦 二叉树简介 二叉树&#xff08;Binary Tree&#xff09;是一种特殊的树形…

【Spring】循环依赖

目录标题 什么是循环依赖循环依赖场景Java SE 演示Spring 容器演示三级缓存核心知识三级缓存四大方法三级缓存中的迁移 三级缓存源码分析源码思维导图 源码图例课前问题推荐阅读 循环依赖是什么&#xff1f;循环依赖的场景有哪一些&#xff1f;三级缓存分别是什么&#xff1f;三…

openEuler学习——mysql(第一次总结)

1、openEuler 二进制方式安装MySQL 8.0.x。 思路是先从官网获取安装包链接如下https://mirrors.aliyun.com/mysql/MySQL-8.0/mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz 然后解压安装修改权限&#xff0c;可以参考mysql官方网站步骤 [rootopenEuler-node1 ~]# wget -c https:…

DS:循环队列的实现

创作不易&#xff0c;给个三连吧&#xff01;&#xff01; 一、前言 对于循环队列&#xff0c;博主也是源自于一道力扣的OJ题 力扣&#xff1a;循环队列的设置 后来我在网上查过&#xff0c;这个循环队列是有自己的应用场景的&#xff01;&#xff01;并不是出题者为了出题…

网络防火墙综合实验

备注&#xff1a;电信网段15.1.1.0 移动网段14.1.1.0 办公区 11.1.1.0 生产区 10.1.1.0 服务区 13.1.1.0 公网 1.1.1.1 和 2.2.2.2 需求&#xff1a; 1、办公区设备可以通过电信链路和移动链路上网&#xff08;多对多nat&#xff0c;并且需要保留一个公网ip&#xff09; 2、…

<网络安全>《40 网络攻防专业课<第六课 - 木马与防范>》

1 木马 1.1 木马简介 木马是攻击者编写的一段恶意代码&#xff0c;它潜伏在被攻击者的计算机中。攻击者通过这个代码可远程控制被攻击者的计算机&#xff0c;以窃取计算机上的信息或者操作计算机。从本质上讲&#xff0c;木马也是病毒的一种&#xff0c;因此不少用户也把木马…

代码随想录算法训练营DAY20 | 二叉树 (8)

一、LeetCode 701 二叉搜索树中的插入操作 题目链接&#xff1a; 701.二叉搜索树中的插入操作https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/ 思路&#xff1a;见缝插针罢辽。 class Solution {public TreeNode insertIntoBST(TreeNode root, i…

ChatGPT-01 用ChatGPT指令,自学任何领域的系统知识

1. 指令位置 Github仓库&#xff1a;Mr Ranedeer AI Tutor 但是需要开通chatgtp plus版本&#xff0c;并且打开代码解释器 2 使用 学习内容 开始学习 GPT甚至可以给你思考题&#xff0c;给出的答案还能进行评价 配置 通过配置表修改 深度 学习风格 沟通风格 语气风格 …

花费200元,我用全志H616和雪糕棒手搓了一台可UI交互的视觉循迹小车

常见的视觉循迹小车都具备有路径识别、轨迹跟踪、转向避障、自主决策等基本功能&#xff0c;如果不采用红外避障的方案&#xff0c;那么想要完全满足以上这些功能&#xff0c;摄像头、电机、传感器这类关键部件缺一不可&#xff0c;由此一来小车成本也就难以控制了。 但如果&a…

深度学习基础之《TensorFlow框架(3)—TensorBoard》

一、TensorBoard可视化学习 1、TensorFlow有一个亮点就是&#xff0c;我们能看到自己写的程序的可视化效果&#xff0c;这个功能就是TensorBoard 2、TensorFlow可用于训练大规模深度神经网络所需的计算&#xff0c;使用该工具涉及的计算往往复杂而深奥。为了方便TensorFlow程…

C++11---(2)

目录 一、新增容器 1.1、array 1.2、forward_list 1.3、unordered系列 二、右值引用和移动语义 2.1、什么是左值&#xff0c;什么是左值引用 2.2、什么是右值&#xff0c;什么是右值引用 2.3、左值引用和右值引用比较 2.4、右值引用使用场景和意义 2.5、右值引用引用…

EasyRecovery易恢复中文破解版2024最新破解序列号

EasyRecovery易恢复是一款来自美国的数据恢复软件&#xff0c;已有35年&#xff08;或38年&#xff09;的历史。它支持不同存储介质的数据恢复&#xff0c;包括电脑系统硬盘、移动硬盘等&#xff0c;并针对不同的数据丢失原因提供了相应的恢复方案。 EasyRecovery易恢复是一款功…