TypeScript前端上傳檔案到MinIO
TypeScript前端上傳檔案到MinIO
什麼是MinIO?
MinIO 是一款高效能、分散式的物件儲存系統. 它是一款軟體產品, 可以100%的執行在標準硬體。即X86等低成本機器也能夠很好的執行MinIO。
本地Docker部署測試伺服器
```bash docker pull bitnami/minio:latest
MINIO_ROOT_USER最少3個字元
MINIO_ROOT_PASSWORD最少8個字元
第一次執行的時候,服務會自動關閉,手動再次啟動就可以正常運行了.
docker run -itd \ --name minio-server \ -p 9000:9000 \ -p 9001:9001 \ --env MINIO_ROOT_USER="root" \ --env MINIO_ROOT_PASSWORD="123456789" \ --env MINIO_DEFAULT_BUCKETS='images' \ --env MINIO_FORCE_NEW_KEYS="yes" \ --env BITNAMI_DEBUG=true \ bitnami/minio:latest
```
上傳的API
它有3個API可供呼叫:
- putObject 從流上傳
- fPutObject 從檔案上傳
- presignedPutObject 提供一個臨時的上傳連結以供上傳
使用1和2的方式的話,在前端需要暴露出連線MinIO的訪問金鑰,很不安全,而且官方的Js客戶端壓根就沒想過開放給瀏覽器.
而3的話,可以由服務端生成一個臨時的上傳連結提供給前端上傳之用,而無需要暴露訪問MinIO的金鑰,非常的安全,我採用的是第三種方式.
第三種方式,官方有一篇文章: Upload Files Using Pre-signed URLs
TypeScript實現
在TypeScript下,我們可用的有三種方式實現檔案上傳:
需要注意的是: 事實上,後兩種API都是封裝的XMLHttpRequest.
1. XMLHttpRequest
```typescript function xhrUploadFile(file: File, url: string) { const xhr = new XMLHttpRequest(); xhr.open('PUT', url, true); xhr.send(file);
xhr.onload = () => {
if (xhr.status === 200) {
console.log(${file.name} 上傳成功
);
} else {
console.error(${file.name} 上傳失敗
);
}
};
}
```
2. Fetch API
typescript
function fetchUploadFile(file: File, url: string) {
fetch(url, {
method: 'PUT',
body: file,
})
.then((response) => {
console.log(`${file.name} 上傳成功`, response);
})
.catch((error) => {
console.error(`${file.name} 上傳失敗`, error);
});
}
3. Axios
typescript
function axiosUploadFile(file: File, url: string) {
const instance = axios.create();
instance
.put(url, file, {
headers: {
'Content-Type': file.type,
},
})
.then(function (response) {
console.log(`${file.name} 上傳成功`, response);
})
.catch(function (error) {
console.error(`${file.name} 上傳失敗`, error);
});
}
從後端獲取臨時上傳連結
typescript
function retrieveNewURL(file: File, cb: (file: File, url: string) => void) {
const url = `http://localhost:8080/presignedUrl/${file.name}`;
axios.get(url)
.then(function (response) {
cb(file, response.data.data.url);
})
.catch(function (error) {
console.error(error);
});
}
上傳檔案
typescript
function onXhrUploadFile(file?: File) {
console.log('onXhrUploadFile', file);
if (file) {
retrieveNewURL(file, (file, url) => {
xhrUploadFile(file, url);
});
}
}
踩過的坑
1. presignedPutObject
方式上傳提交的方法必須得是PUT
我試過了用POST
去上傳檔案,但是顯然的是:我失敗了.必須得用PUT
去上傳.
2. 直接傳送File
即可
看了不少文章都是這麼幹的: 構造一個FormData
,然後把檔案打進去,如果用putObject
和fPutObject
這兩種方式上傳,這是沒問題的,但是使用presignedPutObject
則是不行的,直接傳送File
就可以了.
typescript
fileUpload(file) {
const url = 'http://example.com/file-upload';
const formData = new FormData();
formData.append('file', file)
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url, formData,config)
}
如果使用以上的方式上傳,檔案頭會被插入一段資料,看起來像是這樣子的:
text
------WebKitFormBoundaryaym16ehT29q60rUx
Content-Disposition: form-data; name="file"; filename="webfonts.zip"
Content-Type: application/zip
它是遵照了 rfc1867 定義的協議.
3. 使用Axios
上傳的時候,需要自己把Content-Type
填寫成為file.type
直接使用XMLHttpRequest
和Fetch API
都會自動填寫成為檔案真實的Content-Type
.而Axios
則不會,需要自己填寫進去,或許是我不會使用Axios
,但是這是一個需要注意的地方,否則在MinIO裡邊的Content-Type
會被填寫成為Axios
預設的Content-Type
,或者是你自己指定的.
示例程式碼
Github: https://github.com/tx7do/minio-typescript-example
- 後端採用go+gin實現,用於呼叫MinIO的API
presignedPutObject
獲取臨時上傳Url. - 前端有React和Vue的實現,要實現進度條和多檔案上傳也是容易的.