前端上传文件到minio
项目有需要前端来直接上传文件到minio,由于这里我没看懂官网的文档,所以直接在网上搜的教程,这里主要是讲述一下我遇到的一些问题
1. 实现步骤
下面的代码是我搜到的比较简短的实现步骤
<template>
<div>
<input type="file" @change="onFileChange" />
<button @click="uploadFile" :disabled="!file">上传</button>
<div v-if="progress > 0">上传进度: {{ progress }}%</div>
<div v-if="error" style="color: red;">错误: {{ error }}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
file: null,
progress: 0,
error: null,
};
},
methods: {
onFileChange(event) {
this.file = event.target.files[0];
},
async uploadFile() {
if (!this.file) return;
const url = 'http://<minio-server-url>/<bucket-name>/<object-name>'; // 替换为实际的 MinIO 地址和目标存储桶
const formData = new FormData();
formData.append('file', this.file);
try {
const response = await axios.put(url, formData, {
headers: {
'Content-Type': this.file.type,
'Authorization': `Bearer <your-access-token>` // 如果需要身份验证,请提供访问令牌
},
onUploadProgress: (progressEvent) => {
this.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
}
});
console.log('上传成功:', response.data);
this.progress = 0; // 重置进度
} catch (err) {
this.error = err.message;
console.error('上传失败:', err);
}
},
},
};
</script>
<style scoped>
/* 添加你的样式 */
</style>
minio-server-url、bucket-name、object-name、your-access-token这里根据自己minio上的设置自行替换
2. 修改
a. 图片破损问题
首先我通过上述的步骤实现了上传,但上传后的图片并不能正常显示,下载后打开显示图片破损
原因: 我们使用的是axios.put上传文件,put请求不需要使用formData,formData适合post请求上传数据,如果用put请求上传文件,文件的原始二进制内容应该直接作为请求体发送,而不是使用formData
这里我们将以下代码进行替换
const formData = new FormData();
formData.append(‘file’, this.file);
替换成
const formData=await this.file.arrayBuffer()
替换后,上传成功并在minio控制台可以预览出来图片,之前一直是加载图片,预览不了
b.拿不到文件链接
上传文件成功后,minio并没有返回他的链接,res.data=‘’, 因此这里我们需要自行构建
const response = await axios.put(url, formData, {
headers: {
'Content-Type': this.file.type,
'Authorization': `Bearer <your-access-token>` // 如果需要身份验证,请提供访问令牌
},
onUploadProgress: (progressEvent) => {
this.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
}
});
const fileUrl='http://<minio-server-url>/<bucket-name>/<object-name>'
console.log('上传成功:', response.data);
c.文件名称
有时上传文件的名称可能会有重名,因此这里可以用时间戳进行一个拼接
const fileName= file.name?`${Date.now()}_${file.name}`:`${Date.now()}_${file.raw.name}`
3.总结代码
以上是用type为file的input作为案例进行演示,如果用组件库,例如element-plus中的el-upload的话,文件内容会嵌套一层
<template>
<el-upload :auto-upload="false" v-bind="$attrs" :disabled="disabled" v-model:file-list="fileList"
:on-preview="handlePictureCardPreview" ref="upload">
//这里的一些不涉及本文章的方法就不再下面的代码中进行赘述了,有需要请参考上篇文章
<template #file="{file}">
<div class="file-list-item cursor-pointer">
<!-- 点击预览 -->
<span @click="handlePictureCardPreview(file)" class="flex-1">{{ file.name }}</span>
<!-- 显示进度条 -->
<el-progress class="mt-2" :stroke-width="3" color="#409eff" v-if="file.status === 'ready' && file.percentage !== 0" :percentage="file.percentage" />
<!-- 显示文件上传状态 -->
<span v-if="file.status === 'success'" style="color: green;"><el-icon><CircleCheck /></el-icon></span>
<span v-if="file.status === 'fail'" style="color: red;"><el-icon><CircleClose /></el-icon></span>
<!-- 删除文件 -->
<el-button class="w-[50px] h-[32px] border-[0] text-[20px] delete-btn" icon="close" @click="handleRemove(file)" />
</div>
</template>
</el-upload>
<el-dialog v-model="dialogVisible">
<img class="w-full" :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
async sureUpload() {
for (const file of this.fileList) {
if (file.status !== 'ready') {
continue
}
let res = await this.uploadEleFile(file)
if (res) {
file.url = res.link
file.name=res.name
}
}
this.$emit('update:modelValue', this.fileList.map(a => { return { url: a.url, name: a.name } }))
},
async uploadEleFile(file){
if(!file) return
const fileName= file.name?`${Date.now()}_${file.name}`:`${Date.now()}_${file.raw.name}`
const url=`${server}:${post}/${bucket}/${fileName}`
const fileData = await file.raw.arrayBuffer();
return new Promise(async (resolve,reject)=>{
let res;
try {
res = await axios.put(url, fileData, {
headers: {
'Content-Type': file.raw.type,
'Authorization': `Bearer ${accessKey}`
},
// 监听上传进度
onUploadProgress: (progressEvent) => {
if (progressEvent.progress) {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
// 更新进度
file.percentage = percentCompleted;
}
},
});
if(res.status===200){
file.status = 'success'
const url=`${server}:${post}/${bucket}/${fileName}`
resolve({link:url,name:file.name})
}
console.log('上传成功:', res);
} catch (error) {
file.status = 'fail'
this.$message.error('上传失败,请重新选择素材进行上传')
reject(error)
}
})
},