一、OSS
1、前期准备
1.1 注册阿里云账号,开启对象存储oss功能,创建一个bucket(百度教程多的是,跟着创建一个就行,创建时注意存储类型是标准存储,读写权限是公共读)
有的在创建桶时读写属性是私有,不能设置为公共,先设置为私有,后面再改就可以。
2、后端准备
2.1引入依赖
java
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
2.2:在application.yml中配置一些必要的属性,并写一个类读取出来(我是这样使用的,将某些特定的数据都写到application.yml文件中,方便更改,当然你也可以直接将他写在类的代码中,只不过找起来可能麻烦点
java
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框架的一些特性来实现配置属性的注入和初始化
java
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public 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;
@Override
public void afterPropertiesSet() throws Exception {
END_POINT = endpoint;
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
}
}
2.4,创建一个接口
java
import org.springframework.web.multipart.MultipartFile;
public interface OssService {
/*
上传图片到Oss
*/
String uploadFileAvatar(MultipartFile file);
}
2.5 OssServiceImpl
类是一个服务类,它封装了与阿里云OSS交互的逻辑,其主要作用是处理文件(特别是图片)上传到阿里云对象存储服务(Object Storage Service, OSS)的逻辑。
java
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 {
@Override
public 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请求的逻辑。
java
/**
* oss上传文件
* @param file
* @return
*/
@PostMapping("/upload")
public R uploadFile(MultipartFile file) {
String url = ossService.uploadFileAvatar(file);
return R.ok().put("url",url);
}
3、前端代码
javascript
<el-form-item label="设备图片">
<!-- action 必选参数,上传的地址 headers设置上传的请求头部 show-file-list 是否显示已上传文件列表
on-success 文件上传成功时的钩子 before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。-->
<el-upload
class="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'),
//minio
uploadUrl: this.$http.adornUrl('/dev/fileoss/minIOUpload'),
tokenInfo: {
'token': this.$cookie.get('token')
},
//上传成功后的钩子
handleAvatarSuccess(res, file) {
//将上传成功后的url
this.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;
},
图片展示
html
<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都是上传图片存储在第三方,前端代码没什么区别,后端代码不同点就是连接的配置不同。
导入依赖
java
<!-- MinIO -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
没有像oss一样将连接信息写在yml里,直接所以普代码都写在控制层。
java
//minIO 文件上传 直接返回图片路径
@PostMapping("/minIOUpload")
public R upload(MultipartFile file) throws IOException {
// 设置 MinIO 连接信息 这里使用API端口 9000 而不是9001
String 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;
// 拼接完整的图片访问路径 URL
String 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);
}
三、本地
本地代码如下:
java
/**
* 本地上传
* @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。这样就能展示了。
html
<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>