SpringBoot整合minio服务(超详细)
windows系统启动minio: 先下载minio.exe, win+r打开minio.exe所在的文件夹,运行minio.exe server D:/myMinio,其中D:/myMinio是存放附件文件的路径
启动后在浏览器输入127.0.0.1:9000进入如下页面,新建Bucket,名称(图中yqxl)要与项目配置中的一致。
一、使用docker部署minio
1、拉取镜像
docker pull minio/minio
2、创建目录
1 2 | mkdir -p /home/minio/config mkdir -p /home/minio/data |
3、创建Minio容器并运行
docker run -p 9000:9000 -p 9090:9090 \ --net=host \ --name minio \ -d --restart=always \ -e "MINIO_ACCESS_KEY=minioadmin" \ -e "MINIO_SECRET_KEY=minioadmin" \ -v /home/minio/data:/data \ -v /home/minio/config:/root/.minio \ minio/minio server \ /data --console-address ":9090" -address ":9000"
4、登录minio控制台
5、创建buckets存储桶测试
创建一个名为public的存储桶(名字可自定义),上传文件。
通过http://ip:9000/存储桶名/文件名访问文件
若出现:
可以将存储桶的访问权限设置为public.
二、SpringBoot整合minio
1、创建minio-demo项目
2、引入pom依赖
1 2 3 4 5 | <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.0.2</version> </dependency> |
3、编写配置文件
在application.yml文件中编写相关配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | server: port: 8081 spring: # 配置文件上传大小限制 servlet: multipart: max-file-size: 200MB max-request-size: 200MB minio: host: http: //127.0.0.1:9000 url: ${minio.host}/${minio.bucket}/ access-key: minioadmin secret-key: minioadmin bucket: public |
4、编写MinioConfig配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | import io.minio.MinioClient; import io.minio.ObjectStat; import io.minio.PutObjectOptions; import io.minio.Result; import io.minio.messages.Bucket; import io.minio.messages.Item; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriUtils; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @Component public class MinioConfig implements InitializingBean { @Value(value = "${minio.bucket}" ) private String bucket; @Value(value = "${minio.host}" ) private String host; @Value(value = "${minio.url}" ) private String url; @Value(value = "${minio.access-key}" ) private String accessKey; @Value(value = "${minio.secret-key}" ) private String secretKey; private MinioClient minioClient; @Override public void afterPropertiesSet() throws Exception { Assert.hasText(url, "Minio url 为空" ); Assert.hasText(accessKey, "Minio accessKey为空" ); Assert.hasText(secretKey, "Minio secretKey为空" ); this .minioClient = new MinioClient( this .host, this .accessKey, this .secretKey); } /** * 上传 */ public String putObject(MultipartFile multipartFile) throws Exception { // bucket 不存在,创建 if (!minioClient.bucketExists( this .bucket)) { minioClient.makeBucket( this .bucket); } try (InputStream inputStream = multipartFile.getInputStream()) { // 上传文件的名称 String fileName = multipartFile.getOriginalFilename(); // PutObjectOptions,上传配置(文件大小,内存中文件分片大小) PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE); // 文件的ContentType putObjectOptions.setContentType(multipartFile.getContentType()); minioClient.putObject( this .bucket, fileName, inputStream, putObjectOptions); // 返回访问路径 return this .url + UriUtils.encode(fileName, StandardCharsets.UTF_8); } } /** * 文件下载 */ public void download(String fileName, HttpServletResponse response){ // 从链接中得到文件名 InputStream inputStream; try { MinioClient minioClient = new MinioClient(host, accessKey, secretKey); ObjectStat stat = minioClient.statObject(bucket, fileName); inputStream = minioClient.getObject(bucket, fileName); response.setContentType(stat.contentType()); response.setCharacterEncoding( "UTF-8" ); response.setHeader( "Content-Disposition" , "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8" )); IOUtils.copy(inputStream, response.getOutputStream()); inputStream.close(); } catch (Exception e){ e.printStackTrace(); System. out .println( "有异常:" + e); } } /** * 列出所有存储桶名称 * * @return * @throws Exception */ public List<String> listBucketNames() throws Exception { List<Bucket> bucketList = listBuckets(); List<String> bucketListName = new ArrayList<>(); for (Bucket bucket : bucketList) { bucketListName.add(bucket.name()); } return bucketListName; } /** * 查看所有桶 * * @return * @throws Exception */ public List<Bucket> listBuckets() throws Exception { return minioClient.listBuckets(); } /** * 检查存储桶是否存在 * * @param bucketName * @return * @throws Exception */ public boolean bucketExists(String bucketName) throws Exception { boolean flag = minioClient.bucketExists(bucketName); if (flag) { return true ; } return false ; } /** * 创建存储桶 * * @param bucketName * @return * @throws Exception */ public boolean makeBucket(String bucketName) throws Exception { boolean flag = bucketExists(bucketName); if (!flag) { minioClient.makeBucket(bucketName); return true ; } else { return false ; } } /** * 删除桶 * * @param bucketName * @return * @throws Exception */ public boolean removeBucket(String bucketName) throws Exception { boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<Item>> myObjects = listObjects(bucketName); for (Result<Item> result : myObjects) { Item item = result. get (); // 有对象文件,则删除失败 if (item.size() > 0) { return false ; } } // 删除存储桶,注意,只有存储桶为空时才能删除成功。 minioClient.removeBucket(bucketName); flag = bucketExists(bucketName); if (!flag) { return true ; } } return false ; } /** * 列出存储桶中的所有对象 * * @param bucketName 存储桶名称 * @return * @throws Exception */ public Iterable<Result<Item>> listObjects(String bucketName) throws Exception { boolean flag = bucketExists(bucketName); if (flag) { return minioClient.listObjects(bucketName); } return null ; } /** * 列出存储桶中的所有对象名称 * * @param bucketName 存储桶名称 * @return * @throws Exception */ public List<String> listObjectNames(String bucketName) throws Exception { List<String> listObjectNames = new ArrayList<>(); boolean flag = bucketExists(bucketName); if (flag) { Iterable<Result<Item>> myObjects = listObjects(bucketName); for (Result<Item> result : myObjects) { Item item = result. get (); listObjectNames.add(item.objectName()); } } return listObjectNames; } /** * 删除一个对象 * * @param bucketName 存储桶名称 * @param objectName 存储桶里的对象名称 * @throws Exception */ public boolean removeObject(String bucketName, String objectName) throws Exception { boolean flag = bucketExists(bucketName); if (flag) { List<String> objectList = listObjectNames(bucketName); for (String s : objectList) { if (s. equals (objectName)){ minioClient.removeObject(bucketName, objectName); return true ; } } } return false ; } /** * 文件访问路径 * * @param bucketName 存储桶名称 * @param objectName 存储桶里的对象名称 * @return * @throws Exception */ public String getObjectUrl(String bucketName, String objectName) throws Exception { boolean flag = bucketExists(bucketName); String url = "" ; if (flag) { url = minioClient.getObjectUrl(bucketName, objectName); } return url; } } |
5、编写MinioController类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import com.minio.demo.config.MinioConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.util.List; @RestController @CrossOrigin @RequestMapping( "/test" ) public class MinioController { @Autowired MinioConfig minioConfig; // 上传 @PostMapping( "/upload" ) public Object upload(@RequestParam( "file" ) MultipartFile multipartFile) throws Exception { return this .minioConfig.putObject(multipartFile); } // 下载文件 @GetMapping( "/download" ) public void download(@RequestParam( "fileName" )String fileName, HttpServletResponse response) { this .minioConfig.download(fileName,response); } // 列出所有存储桶名称 @PostMapping( "/list" ) public List<String> list() throws Exception { return this .minioConfig.listBucketNames(); } // 创建存储桶 @PostMapping( "/createBucket" ) public boolean createBucket(String bucketName) throws Exception { return this .minioConfig.makeBucket(bucketName); } // 删除存储桶 @PostMapping( "/deleteBucket" ) public boolean deleteBucket(String bucketName) throws Exception { return this .minioConfig.removeBucket(bucketName); } // 列出存储桶中的所有对象名称 @PostMapping( "/listObjectNames" ) public List<String> listObjectNames(String bucketName) throws Exception { return this .minioConfig.listObjectNames(bucketName); } // 删除一个对象 @PostMapping( "/removeObject" ) public boolean removeObject(String bucketName, String objectName) throws Exception { return this .minioConfig.removeObject(bucketName, objectName); } // 文件访问路径 @PostMapping( "/getObjectUrl" ) public String getObjectUrl(String bucketName, String objectName) throws Exception { return this .minioConfig.getObjectUrl(bucketName, objectName); } } |
6、测试(上传)
开发中上传接口用得较多,其他接口可自行测试。
三、Vue+Element-ui前端交互
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | # npm下载element-ui npm install element-ui -S // 引入ElementUI import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) <template> <div id= "app" > <el-upload class = "avatar-uploader" action= "http://127.0.0.1:8081/test/upload" :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> </div> </template> <script> export default { data() { return { imageUrl: '' }; }, methods: { handleAvatarSuccess(res, file) { this .imageUrl = URL.createObjectURL(file.raw); }, beforeAvatarUpload(file) { const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' ; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this .$message.error( '上传头像图片只能是 jpg或png 格式!' ); } if (!isLt2M) { this .$message.error( '上传头像图片大小不能超过 2MB!' ); } return isJPG && isLt2M; } } } </script> <style lang= "less" > .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; left: 200px; top: 120px; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409EFF; } .avatar-uploader-icon { position: relative; top: 80px; font-size: 28px; color: #8c939d; width: 389px; height: 204px; line-height: 178px; text-align: center; } .avatar { width: 389px; height: 204px; display: block; } </style |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了