普通上傳:
1:前端上傳到後端(後端服務器帶寬限制,極大影響性能)
2:後端保存到文件服務器(同步/異步)
3:後端保存到數據庫
簽名直傳:
1:前端從後端獲取簽名
2:前端根據簽名上傳到文件服務器(不走後端)
3:前端返回後端URL保存到數據庫
首先保障文件上傳服務可以使用
可以搭建一個Minio服務器
docker run -p 9000:9000 --name minio1 \
> -e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
> -e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
> -v /mnt/data:/data \
> -v /mnt/config:/root/.minio \
> minio/minio server /data
使用過程中出現的坑總結(沒有問題忽略下面兩條)
1:minio上傳大文件錯誤
413:Request Entity Too Large
原因
文件太大超Ingress限制
解決
創建 ingress 時添加 annotations(註釋)
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 1024m
2:
一般bucket公共讀的是不需要添加後綴就可以訪問,一般私有的bucket是需要添加的後綴。
出現了奇怪的問題:添加後綴不可以訪問,不添加後綴反而可以訪問
排查:兩處問題:
1: bucket權限錯誤,設成共有的了(需要私有)
2:獲取簽名出錯
看了看代碼objectClient().getSignedURL方法出錯
簽名直傳
首先,前端調用後端接口獲取簽名
簽名直傳後端:
public ResultModel getObsSign(@RequestParam Map<String, String> paramMap) {
ResultModel resultModel = new ResultModel();
int defaultTTLSeconds = 60 * 60;
Long maxLength = 100 * 1024L;
PostPolicyRequest postPolicyRequest = new PostPolicyRequest(defaultTTLSeconds, maxLength, obsAttributeConfig.getBucket());
PostPolicyInfo postPolicyInfo = objectClient.generatePostPolicy(postPolicyRequest);
resultModel.setData(postPolicyInfo);
return resultModel;
}
拿到簽名信息後,爲了確保正確,可以在cmd中進行測試
curl -X POST http://minio.glodon.com/bkqw \
-H 'content-type: multipart/form-data' \
-F 'key=2.txt' \
-F policy=eyJleHBpcmF0aW9uIjoiMjAyMC0wMi0wNVQyMzoxNTo0OC4wMTZaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTAwMDBdLHsiYnVja2V0IjoiYmtxdyJ9LHsic3VjY2Vzc19hY3Rpb25fc3RhdHVzIjoiMjAwIn0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIiXV19 \
-F AWSAccessKeyId=AKIAICSFADNN8EXAMBLE \
-F signature=Ezl5w6g1OJMWjDhxN42IPcSk8WA= \
-F success_action_status=200 \
-F 'file=@D:\1.txt'
出現下面響應代表簽名獲得的是沒問題的(注意此處對文件大小的限制)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 986 0 0 100 986 0 962k --:--:-- --:--:-- --:--:-- 962k
前端代碼開發
util.js
import request from '@/utils/request'
// 獲取簽名
export function getSign(applicationId) {
return request({
url: '/application/' + applicationId + '/sign',
method: 'get'
})
}
// 上傳
export function uploadBySign(params, host) {
return request({
url: host,
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data: params
})
}
上傳圖片
// 前端選中圖片
handleChange(file) {
this.fileList = []
this.fileList.push(file)
this.file = file
const fileFormat = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length)
if (fileFormat.toLowerCase() !== 'jpg' && fileFormat.toLowerCase() !== 'png' && fileFormat.toLowerCase() !== 'jpeg' && fileFormat.toLowerCase() !== 'svg') {
this.file = ''
this.$message.error('僅支持.png .jpg .jpeg格式')
this.$refs['my-upload'].clearFiles()
this.fileList = []
return
}
var obsData = new FormData()
obsData.append('key', this.file.name)
obsData.append('policy', this.sign.encodedPolicy)
obsData.append('AWSAccessKeyId', this.sign.accessId)
obsData.append('success_action_status', 200)
obsData.append('signature', this.sign.postSignature)
obsData.append('file', this.file.raw, this.file.name)
uploadBySign(obsData, this.sign.host).then(response => {
// 省略
}).catch(() => {
this.$message.error('保存失敗!')
})
}