Minio
Minio
1 基于centos7,docker部署
# 直接使用centos7部署,但是只能通过私有ip访问本机,指定公网ip不行 cd /usr/local/ # 获取minio wget -q http://dl.minio.org.cn/server/minio/release/linux-amd64/minio # 授予文件权限 chmod +x minio #启动minio server服务,指定数据存储目录/mnt/data,最好是指定控制台端口,这个方式创建的minio只能通过本机访问。 ./minio server /mnt/data --console-address :9090 #docker 成功 #9090是console中控制台访问,9000是容器中api的端口,java代码用的是api的端口 docker run -d -p 9000:9000 -p 9090:9090 --name docker_minio -e "MINIO_ROOT_USER=fengpeng" -e "MINIO_ROOT_PASSWORD=fengpeng" -v /usr/local/minio/data:/data -v /usr/local/minio/config:/root/.minio #/data 目录设置为MinIO服务器要管理的对象存储数据的目录(-v /usr/local/minio/data:/data ) # minio/minio 是一个 Docker 镜像,用于启动 MinIO 服务器 minio/minio server /data --console-address ":9090" # 如果有错误,通过docker logs 容器ip查看日志
完整语句
docker run -d -p 9000:9000 -p 9090:9090 --name docker_minio -e "MINIO_ROOT_USER=fengpeng" -e "MINIO_ROOT_PASSWORD=fengpeng" -v /usr/local/minio/data:/data -v /usr/local/minio/config:/root/.minio minio/minio server /data --console-address ":9090"
2 springboot整合minio
pom.xml
<!--minio依赖--> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.1</version> </dependency> <!--minio相关依赖--> <dependency> <groupId>me.tongfei</groupId> <artifactId>progressbar</artifactId> <version>0.9.5</version> </dependency> <!--minio相关依赖--> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>5.0.0-alpha.10</version> </dependency>
application.yml
#minio配置 minio: endpoint: http://8.137.103.17:9000 accessKey: secretKey: bucketName: test
MinioController
package com.feng.controller; import com.alibaba.fastjson2.JSON; import com.feng.utils.DataResult; import io.minio.*; import io.minio.errors.*; import io.minio.messages.Item; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.utils.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; /** * @author fengpeng * @version V1.0 * Copyright (c) 2024, fengpeng@hwadee.com All Rights Reserved. * @ProjectName:big_event * @Title: MinioController * @Package com.feng.controller * @Description: minio控制类 * @date 2024/1/25 9:35 */ @RestController @RequestMapping("/minio") @Slf4j public class MinioController { @Autowired private MinioClient minioClient; @Value("${minio.bucketName}") private String bucketName; @Value("${minio.endpoint}") private String endpoint; @GetMapping("/list") public List<Object> list() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { //获取bucket列表 Iterable<Result<Item>> myObjects = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build()); Iterator<Result<Item>> iterator = myObjects.iterator(); List<Object> items = new ArrayList<>(); String format = "{'fileName':'%s', 'fileSize':'%s'}"; while (iterator.hasNext()){ Item item = iterator.next().get(); items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size())))); } return items; } @PostMapping("/upload") public DataResult upload(@RequestParam(name = "file",required = false) MultipartFile[] files) throws Exception { if (Objects.isNull(files) || files.length == 0){ return DataResult.error("请选择文件"); } List<String> orgFileNameList = new ArrayList<>(files.length); for (MultipartFile multipartFile : files) { //获取文件名 String orgFileName = multipartFile.getOriginalFilename(); //用时间作为不重复的名字 Date date = new Date(System.currentTimeMillis()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String fileName = sdf.format(date) + orgFileName.substring(orgFileName.lastIndexOf(".")); orgFileNameList.add(fileName); try { InputStream in = multipartFile.getInputStream(); minioClient.putObject( PutObjectArgs.builder().bucket(bucketName) .object(fileName) .stream(in, multipartFile.getSize(),-1) .contentType(multipartFile.getContentType()) .build()); in.close(); } catch (Exception e) { log.error(e.getMessage()); return DataResult.error("上传失败"); } } /*Map<String, Object> data = new HashMap<>(); data.put("bucketName", bucketName); //默认获取第一个文件 data.put("fileName", orgFileNameList.get(0));*/ /*记得将minio的console的bucket权限设置为public*/ String fileUrl = endpoint + "/" + bucketName + "/" + orgFileNameList.get(0); return DataResult.success(fileUrl); } @GetMapping("/download") public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) throws Exception { InputStream in = null; try { //获取对象信息 StatObjectResponse stat = minioClient.statObject (StatObjectArgs.builder().bucket(bucketName).object(fileName).build()); //设置响应头 response.setContentType(stat.contentType()); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); //文件下载 in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build()); IOUtils.copy(in, response.getOutputStream()); }catch (Exception e){ log.error(e.getMessage()); }finally { if (in != null) { try { in.close(); } catch (IOException e) { log.error(e.getMessage()); } } } } @DeleteMapping("/delete/{fileName}") public DataResult delete(@PathVariable("fileName") String fileName) throws Exception { try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build()); }catch (Exception e){ log.error(e.getMessage()); return DataResult.error("删除文件失败"); } return DataResult.success(); } private static String formatFileSize(long fileS) { DecimalFormat df = new DecimalFormat("#.00"); String fileSizeString = ""; String wrongSize = "0B"; if (fileS == 0) { return wrongSize; } if (fileS < 1024) { fileSizeString = df.format((double) fileS) + " B"; } else if (fileS < 1048576) { fileSizeString = df.format((double) fileS / 1024) + " KB"; } else if (fileS < 1073741824) { fileSizeString = df.format((double) fileS / 1048576) + " MB"; } else { fileSizeString = df.format((double) fileS / 1073741824) + " GB"; } return fileSizeString; } }
前端代码
UserAvatar.vue
<script setup> import { Plus, Upload } from '@element-plus/icons-vue' import {ref} from 'vue' import avatar from '@/assets/default.png' const uploadRef = ref() import {useTokenStore} from '@/stores/token.js' const tokenStore = useTokenStore() import useUserInfoStore from '@/stores/userInfo.js' const userInfoStore = useUserInfoStore() //用户头像地址 const imgUrl= ref(userInfoStore.info.userPic) //图片上传成功的回调函数 const uploadSuccess = (res) => { imgUrl.value = res.data } import {updateAvatarUpdateService} from '@/api/user.js' import {ElMessage} from 'element-plus' //头像修改 const updateAvatar = async() => { //调用接口 let url = imgUrl.value let res = await updateAvatarUpdateService(url.slice(url.lastIndexOf('/') + 1)) ElMessage.success(res.message ? res.message : '修改成功') //修改pinia中的数据 userInfoStore.info.userPic = imgUrl.value } </script> <template> <el-card class="page-container"> <template #header> <div class="header"> <span>更换头像</span> </div> </template> <el-row> <el-col :span="12"> <el-upload ref="uploadRef" class="avatar-uploader" :show-file-list="false" :auto-upload="true" action="/api/minio/upload" name="file" :headers="{'Authorization': tokenStore.token}" :on-success="uploadSuccess" > <!-- 选择图片后,自动上传到服务器,返回图片在服务器上的地址 --> <img v-if="imgUrl" :src="imgUrl" class="avatar" /> <!-- 默认图片 --> <img v-else :src="avatar" width="278" /> </el-upload> <br /> <el-button type="primary" :icon="Plus" size="large" @click="uploadRef.$el.querySelector('input').click()"> 选择图片 </el-button> <el-button type="success" :icon="Upload" size="large" @click="updateAvatar"> 上传头像 </el-button> </el-col> </el-row> </el-card> </template> <style lang="scss" scoped> .avatar-uploader { :deep() { .avatar { width: 278px; height: 278px; display: block; } .el-upload { border: 1px dashed var(--el-border-color); border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; transition: var(--el-transition-duration-fast); } .el-upload:hover { border-color: var(--el-color-primary); } .el-icon.avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 278px; height: 278px; text-align: center; } } } </style>
注意
不设置成Public,无法直接通过返回的图片url访问bucket中的图片
岁月如歌,,,
本文作者:千夜ん
本文链接:https://www.cnblogs.com/fengpeng123/p/18024146
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步