一、OSS
1、前期准备
1.1 注册阿里云账号,开启对象存储oss功能,创建一个bucket(百度教程多的是,跟着创建一个就行,创建时注意存储类型是标准存储,读写权限是公共读)
有的在创建桶时读写属性是私有,不能设置为公共,先设置为私有,后面再改就可以。
2、后端准备
2.1引入依赖
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.10.2</version>
</dependency>
2.2:在application.yml中配置一些必要的属性,并写一个类读取出来(我是这样使用的,将某些特定的数据都写到application.yml文件中,方便更改,当然你也可以直接将他写在类的代码中,只不过找起来可能麻烦点
bookstore:alioss:endpoint: xxxxx #外网访问的地域节点access-key-id: xxxxxx #访问阿里云api的秘钥access-key-secret: xxxxxxx #访问阿里云api的秘钥bucket-name: xxxxxxxx #bucket-name//写在application.yml文件中,这些数据在阿里云中可以找到,秘钥在自己的账号主页里创建生成,地域节点和bucket-name都在创建好bucket后查看
2.3 OssUtil
是一个用于处理阿里云对象存储服务(Object Storage Service, OSS)的工具类,它利用了Spring框架的一些特性来实现配置属性的注入和初始化
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Data@Componentpublic class OssUtil implements InitializingBean {@Value("${aliyun.oss.file.endpoint}")private String endpoint;@Value("${aliyun.oss.file.keyid}")private String keyId;@Value("${aliyun.oss.file.keysecret}")private String keySecret;@Value("${aliyun.oss.file.bucketname}")private String bucketName;public static String END_POINT;public static String ACCESS_KEY_ID;public static String ACCESS_KEY_SECRET;public static String BUCKET_NAME;@Overridepublic void afterPropertiesSet() throws Exception {END_POINT = endpoint;ACCESS_KEY_ID = keyId;ACCESS_KEY_SECRET = keySecret;BUCKET_NAME = bucketName;}}
2.4,创建一个接口
import org.springframework.web.multipart.MultipartFile;public interface OssService {/*上传图片到Oss*/String uploadFileAvatar(MultipartFile file);}
2.5 OssServiceImpl
类是一个服务类,它封装了与阿里云OSS交互的逻辑,其主要作用是处理文件(特别是图片)上传到阿里云对象存储服务(Object Storage Service, OSS)的逻辑。
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.wedu.common.utils.OssUtil;
import com.wedu.modules.dev.service.OssService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;@Service
public class OssServiceImpl implements OssService {@Overridepublic String uploadFileAvatar(MultipartFile file) {/*** 上传图片到oss** @param file 文件对象* @return*/if (file == null || file.isEmpty()) {return "文件为空,请选择文件后再上传。";}// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。String endpoint = OssUtil.END_POINT;// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。String accessKeyId = OssUtil.ACCESS_KEY_ID;String accessKeySecret = OssUtil.ACCESS_KEY_SECRET;String bucketName = OssUtil.BUCKET_NAME;try {// 获取文件名称String realName = file.getOriginalFilename();// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。InputStream inputStream = file.getInputStream();// 依次填写Bucket名称(例如examplebucket)和Object完整路径(例如exampledir/exampleobject.txt)。Object完整路径中不能包含Bucket名称。ossClient.putObject(bucketName, realName, inputStream);// 关闭OSSClient。ossClient.shutdown();String url = "https://" + bucketName + "." + endpoint + "/" + realName;return url;} catch (Exception e) {e.printStackTrace();return null;}}}
2.6 Spring MVC的控制器方法,它定义了一个处理文件上传的HTTP POST请求的逻辑。
/*** oss上传文件* @param file* @return*/@PostMapping("/upload")public R uploadFile(MultipartFile file) {String url = ossService.uploadFileAvatar(file);return R.ok().put("url",url);}
3、前端代码
<el-form-item label="设备图片"><!-- action 必选参数,上传的地址 headers设置上传的请求头部 show-file-list 是否显示已上传文件列表on-success 文件上传成功时的钩子 before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。--><el-uploadclass="avatar-uploader":action= "uploadUrl":headers= "tokenInfo":show-file-list="false":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload"><img v-if="imageUrl" :src="imageUrl" class="avatar"><i v-else class="el-icon-plus avatar-uploader-icon"></i></el-upload></el-form-item>data () {return {imageUrl:'',//oss// uploadUrl: this.$http.adornUrl('/dev/fileoss/upload'),//本地上传接口// uploadUrl: this.$http.adornUrl('/dev/fileoss/uploadLocal'),//miniouploadUrl: this.$http.adornUrl('/dev/fileoss/minIOUpload'),tokenInfo: {'token': this.$cookie.get('token')},//上传成功后的钩子handleAvatarSuccess(res, file) {//将上传成功后的urlthis.dataForm.image = res.url;this.imageUrl = URL.createObjectURL(file.raw);},beforeAvatarUpload(file) {const isJPG = file.type === 'image/jpeg';const isLt2M = file.size / 1024 / 1024 < 2;if (!isJPG) {this.$message.error('上传头像图片只能是 JPG 格式!');}if (!isLt2M) {this.$message.error('上传头像图片大小不能超过 2MB!');}return isJPG && isLt2M;},
图片展示
<el-table-column prop="image" header-align="center" align="center" label="设备图片"><template slot-scope="scope"><img v-if="scope.row.image":src=" scope.row.image"style="width: 50px; height: 50px;" class="avatar"></template></el-table-column>
二、Minio
前期的安装准备参考这位博主的MinIO安装与启动【windows】_minio启动-CSDN博客, minio跟oss都是上传图片存储在第三方,前端代码没什么区别,后端代码不同点就是连接的配置不同。
导入依赖
<!-- MinIO --><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version></dependency>
没有像oss一样将连接信息写在yml里,直接所以普代码都写在控制层。
//minIO 文件上传 直接返回图片路径@PostMapping("/minIOUpload")public R upload(MultipartFile file) throws IOException {// 设置 MinIO 连接信息 这里使用API端口 9000 而不是9001String endpoint = "http://192.168.255.1:9000";String accessKey = "你自己的账户名";String secretKey = "密码";String bucketName = "桶的名称";String bucketUrl = "http://192.168.255.1:9000/" + bucketName; // MinIO bucket URL// 获取当前时间戳String flag = System.currentTimeMillis() + " ";// 获取原始文件名(就是你上传的文件本身的名字)String originalFilename = file.getOriginalFilename();// 生成新的文件名(时间戳+原始文件名) 避免文件名相同被覆盖String fileName = flag + originalFilename;// 拼接完整的图片访问路径 URLString url = bucketUrl + "/" + fileName;try {// 创建 MinioClient 实例MinioClient minioClient = new MinioClient.Builder().endpoint(endpoint).credentials(accessKey, secretKey).build();// 构建文件上传相关信息PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(file.getInputStream(), file.getSize(), -1)//application/octet-stream 告诉浏览器这是一个附件,浏览器会直接进行下载,而不是预览.contentType(("application/octet-stream")).build();// 将文件上传到 MinIO 服务器minioClient.putObject(args);}catch (Exception e) {throw new ServerException("文件上传异常: " + e.getMessage(), e);}return R.ok().put("url", url);}
三、本地
本地代码如下:
/*** 本地上传* @param file* @return*/@PostMapping("/uploadLocal")public R uploadFileLocal(MultipartFile file) {//获取当前时间戳String flag = System.currentTimeMillis() + " ";//获取原始文件名(就是你上传的文件本身的名字)String fileName = file.getOriginalFilename();try {//如果没有file文件夹,在项目根目录创建一个file文件夹if (!FileUtil.isDirectory(filePath)) {FileUtil.mkdir(filePath);}//文件存储形式:时间戳+文件名String destFileName =filePath + flag + fileName;// FileUtil.writeBytes(file.getBytes(), filePath + flag + fileName);File destFile = new File(destFileName);destFile.getParentFile();file.transferTo(destFile);Thread.sleep((1L));} catch (Exception e) {System.err.println(fileName + "--文件上传失败");}return R.ok().put("url", flag);}//本地下载@GetMapping("/{flag}")public void picturePath(@PathVariable String flag, HttpServletResponse response) {//用于向客户端写入数据的输出流OutputStream os;//存储文件名的列表。List<String> fileNames = FileUtil.listFileNames(filePath);//存储匹配标志的文件名。//使用Java 8的Stream API,从fileNames列表中筛选出包含flag的文件名。如果找到匹配项,则使用findAny().orElse("")获取匹配的文件名,否则返回空字符串。String picture = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");try {//如果picture不为空(即找到了匹配的文件名)if (StrUtil.isNotEmpty(picture)) {response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(picture, "UTF-8"));//响应的内容类型为application/octet-stream,确保浏览器不会尝试打开文件response.setContentType(("application/octet-stream"));//声明了一个字节数组bytes,用于存储文件的二进制内容byte[] bytes = FileUtil.readBytes(filePath + picture);os = response.getOutputStream();//将字节数组写入输出流,发送给客户端os.write(bytes);os.flush();os.close();}} catch (Exception e) {System.out.println("文件下载失败");}}
注意:前端代码需要改变一下,不然上传的图片是看不了的,回显的方法通过之前的前端代码是不会调用这和方法。需要在前端拼接一下url。这样就能展示了。
<el-table-column prop="image" header-align="center" align="center" label="设备图片"><template slot-scope="scope"><img v-if="scope.row.image":src="'http://localhost:8080/wedu/dev/fileoss/'+ scope.row.image"style="width: 50px; height: 50px;" class="avatar"> </template></el-table-column>