赞
踩
如果框架默认存储使用的本地磁盘(这里举例若依脚手架),对于一些文件较大较多且有数据备份、数据安全、分布式等等就满足不了我们的要求,对于这种情况我们可以集成OSS对象存储服务。minio是目前github上star最多的数据存储框架。minio可以用来搭建分布式存储服务,可以很好的和机器学习相结合。
1.ruoyi-common/pom.xml文件添加minio依赖。
<!-- Minio 文件存储 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
2.在CommonController.java自定义Minio服务器上传请求,这里我们只需把uploadUtils后调用的方法改成我们自己的方法就行。url是为了配合本地存储的路径,这里我们只需返回两个一样就可以。
另外一种方式也可以在通知公告新增和修改页面将文件上传的路径common/upload修改为common/uploadMinio,然后上传图片测试验证结果。这个就是重写一个接口,原理一样,因为本人对前端不是太熟悉,这里不做介绍
/** * 通用上传请求 */ @PostMapping("/common/upload") public AjaxResult uploadFile(MultipartFile file) throws Exception { try { // 上传文件路径 String filePath = RuoYiConfig.getUploadPath(); // 上传并返回新文件名称 String fileName = uploadUtils.uploadMinio(file,"wsp"); String url = serverConfig.getUrl() + fileName; AjaxResult ajax = AjaxResult.success(); ajax.put("fileName", fileName); ajax.put("url", fileName); return ajax; } catch (Exception e) { return AjaxResult.error(e.getMessage()); } }
3.编写uploadMinio方法
/** * 自定义bucketName配置上传到Minio服务器 * * @param file 上传的文件 * @return 文件名称 * @throws Exception */ public final String uploadMinio(MultipartFile file, String bucketName) throws IOException { try { return uploadMinino(bucketName, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); } catch (Exception e) { throw new IOException(e.getMessage(), e); } } /** * 真正的上传方法 */ private final String uploadMinino(String bucketName, MultipartFile file, String[] allowedExtension) throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, InvalidExtensionException, ServerException, InsufficientDataException, NoSuchAlgorithmException, InternalException, InvalidResponseException, XmlParserException, InvalidKeyException, ErrorResponseException { //判断是否有这个桶 if (!minioUtils.bucketExists(bucketName)) { minioUtils.createBucket(bucketName); } //桶下是否有这个目录 if (!minioUtils.doesFolderExist(bucketName, getExtension(file))) { minioUtils.putDirObject(bucketName, getExtension(file) + "/"); } //判断文件长度 int fileNamelength = file.getOriginalFilename().length(); if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); } //判断文件大小 assertAllowed(file, allowedExtension); try { String fileName = extractFilename(file); if (getExtension(file).equals("jpg") || getExtension(file).equals("png") || getExtension(file).equals("jpg")){ minioUtils.putObject(bucketName, file,getExtension(file) + "/" + fileName,MimeTypeUtils.IMAGE_JPG); } else { minioUtils.putObject(bucketName, getExtension(file) + "/" + fileName,file.getInputStream()); } return ACCESS_URL + "/" + bucketName + "/" + getExtension(file) + "/" + fileName; } catch (Exception e) { throw new IOException(e.getMessage(), e); } }
还需要一个Minio操作工具类,这里为了以后DML操作,功能比较齐,如果文件服务器里的东西不需要删除,只写一个增加即可。
@Component public class MinioUtils { private static final String ENDPOINT = "http://localhost:9000"; private static final String ACCESS_KEY = "minioadmin"; private static final String SECRET_KEY = "minioadmin"; private MinioClient minioClient; @PostConstruct public void init() { this.minioClient = MinioClient.builder().endpoint(ENDPOINT) .credentials(ACCESS_KEY, SECRET_KEY).build(); } /** * 验证bucketName是否存在 * * @return boolean true:存在 */ public boolean bucketExists(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } /** * 创建bucket * * @param bucketName bucket名称 */ public void createBucket(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); setBucketPolicy(bucketName); } } /** * 设置 bucket 的访问策略 * * @param bucketName 桶名称 */ public void setBucketPolicy(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { String config = " {\n" + " \"Statement\": [\n" + " {\n" + " \"Action\": [\n" + " \"s3:GetBucketLocation\",\n" + " \"s3:ListBucket\"\n" + " ],\n" + " \"Effect\": \"Allow\",\n" + " \"Principal\": \"*\",\n" + " \"Resource\": \"arn:aws:s3:::" + bucketName + "\"\n" + " },\n" + " {\n" + " \"Action\": \"s3:GetObject\",\n" + " \"Effect\": \"Allow\",\n" + " \"Principal\": \"*\",\n" + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + " }\n" + " ],\n" + " \"Version\": \"2012-10-17\"\n" + "}"; minioClient.setBucketPolicy(SetBucketPolicyArgs.builder() .bucket(bucketName) .config(config) .build()); } /** * 获取存储桶策略 * * @param bucketName 存储桶名称 * @return json */ private JSONObject getBucketPolicy(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, BucketPolicyTooLargeException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InsufficientDataException, ErrorResponseException { String bucketPolicy = minioClient .getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); return JSONObject.parseObject(bucketPolicy); } /** * 获取全部bucket * <p> * https://docs.minio.io/cn/java-client-api-reference.html#listBuckets */ public List<Bucket> getAllBuckets() throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.listBuckets(); } /** * 根据bucketName获取信息 * * @param bucketName bucket名称 */ public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 根据bucketName删除信息 * * @param bucketName bucket名称 */ public void removeBucket(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); } /** * 判断文件是否存在 * * @param bucketName 存储桶 * @param objectName 对象 * @return true:存在 */ public boolean doesObjectExist(String bucketName, String objectName) { boolean exist = true; try { minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } catch (Exception e) { exist = false; } return exist; } /** * 判断文件夹是否存在 * * @param bucketName 存储桶 * @param objectName 文件夹名称(去掉/) * @return true:存在 */ public boolean doesFolderExist(String bucketName, String objectName) { boolean exist = false; try { Iterable<Result<Item>> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build()); for (Result<Item> result : results) { Item item = result.get(); if (item.isDir() && objectName.equals(item.objectName())) { exist = true; } } } catch (Exception e) { exist = false; } return exist; } /** * 根据文件前置查询文件 * * @param bucketName bucket名称 * @param prefix 前缀 * @param recursive 是否递归查询 * @return MinioItem 列表 */ public List<Item> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException { List<Item> list = new ArrayList<>(); Iterable<Result<Item>> objectsIterator = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build()); if (objectsIterator != null) { for (Result<Item> o : objectsIterator) { Item item = o.get(); list.add(item); } } return list; } /** * 获取文件流 * * @param bucketName bucket名称 * @param objectName 文件名称 * @return 二进制流 */ public InputStream getObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 断点下载 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param offset 起始字节的位置 * @param length 要读取的长度 * @return 流 */ public InputStream getObject(String bucketName, String objectName, long offset, long length) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.getObject( GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length) .build()); } /** * 获取路径下文件列表 * * @param bucketName bucket名称 * @param prefix 文件名称 * @param recursive 是否递归查找,如果是false,就模拟文件夹结构查找 * @return 二进制流 */ public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) { return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build()); } /** * 通过MultipartFile,上传文件 * * @param bucketName 存储桶 * @param file 文件 * @param objectName 对象名 */ public ObjectWriteResponse putObject(String bucketName, MultipartFile file, String objectName, String contentType) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { InputStream inputStream = file.getInputStream(); return minioClient.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType) .stream(inputStream, inputStream.available(), -1) .build()); } /** * 上传本地文件 * * @param bucketName 存储桶 * @param objectName 对象名称 * @param fileName 本地文件路径 */ public ObjectWriteResponse putObject(String bucketName, String objectName, String fileName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.uploadObject( UploadObjectArgs.builder() .bucket(bucketName).object(objectName).filename(fileName).build()); } /** * 通过流上传文件 * * @param bucketName 存储桶 * @param objectName 文件对象 * @param inputStream 文件流 */ public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream inputStream) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( inputStream, inputStream.available(), -1) .build()); } /** * 创建文件夹或目录 * * @param bucketName 存储桶 * @param objectName 目录路径 */ public ObjectWriteResponse putDirObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.putObject( PutObjectArgs.builder().bucket(bucketName).object(objectName).stream( new ByteArrayInputStream(new byte[]{}), 0, -1) .build()); } /** * 获取文件信息, 如果抛出异常则说明文件不存在 * * @param bucketName bucket名称 * @param objectName 文件名称 */ public StatObjectResponse statObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient .statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 拷贝文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param srcBucketName 目标bucket名称 * @param srcObjectName 目标文件名称 */ public ObjectWriteResponse copyObject(String bucketName, String objectName, String srcBucketName, String srcObjectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { return minioClient.copyObject( CopyObjectArgs.builder() .source(CopySource.builder().bucket(bucketName).object(objectName).build()) .bucket(srcBucketName) .object(srcObjectName) .build()); } /** * 删除文件 * * @param bucketName bucket名称 * @param objectName 文件名称 */ public void removeObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 批量删除文件 * * @param bucketName bucket * @param keys 需要删除的文件列表 */ public boolean removeObjects(String bucketName, List<String> keys) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException { List<DeleteObject> objects = new LinkedList<>(); for (String objectName:keys){ objects.add(new DeleteObject(objectName)); } Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build()); for (Result<DeleteError> result : results) { DeleteError error = result.get(); } return true; } }
这里我是本地搭建的minio服务,具体怎么搭建,请看Minio服务搭建
以上就是后端需要改动的地方,后面是前端需要改动的地方
src/components/Editor/index.vue
src/components/ImageUpload/index.vue
总之,什么地方用到文件上传把地址换成现在的地址,之前是拼的本地路径。
遇到的问题
前端图片不显示问题
先看返回值的路径格式对不对,要是对的话,把路径复制出来在一个新网页上查看,看是否是查看状态,而不是直接下载。如果出现下载状态,是因为在上传的时候没有定义类型,就是响应头不符合,上面可以看到我在上传的时候(uploadMinino方法的try块)判断了文件是什么类型,然后去调用不同的方法。如果是图片是直接查看,文件的话是下载。
文件访问问题
原本框架用的static修饰来做到直接打点去调,而本人习惯把对象交给Spring IOC容器管理,所以在调用的时候可能会报错,这个时候查看这个方法是静态的还是在IOC容器中即可。
Minio删除文件无效问题
minio中的DML操作没有返回值,所以删没删掉我们需要去服务器查看,具体原因请见minio删除无效解决办法
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。