在前端項目中經常遇到上傳文件的需求,ant design 作為 react 的前端框架,提供的 upload 組件為上傳文件提供了很大的方便,官方提供的各種形式的上傳基本上可以覆蓋大多數的場景,但是對於不同的服務器平台,可能實現方式會有所不同,尤其最近使用了阿里雲作為服務器上傳,就需要自定義上傳行為才能滿足需求,因此針對不同平台文件上傳的異同和 upload 組件使用中遇到的問題做一個簡單總結,希望可以對遇到類似問題的小伙伴有所幫助。
首先這里大致總結了幾個不同平台服務器上傳方式的異同:
服務器平台 | 上傳憑證 | 請求方式 method | 文件格式 content-type |
七牛雲 | key、token | POST | multipart/form-data |
騰雲雲 | key、url | POST | multipart/form-data |
阿里雲 | key、url | PUT | application/octet-stream |
upload 組件默認提供的請求方式是POST,並且文件的提交類型是 form-data 格式,因此使用 upload 組件可以直接上傳文件到七牛雲和騰訊雲,但是在上傳到阿里雲的時候,就需要對上傳操作進行配置(需要吐槽一句,同樣是自家的產品,為什么請求方式不統一),為此官方提供了 customRequest 這個 api, 並且FAQ中提供了參考文檔:https://github.com/react-component/upload#customrequest。
接下來看不同的平台具體上傳實現上的基本代碼:
七牛雲上傳方式比較簡單,官方提供了統一的上傳地址 https://upload-z2.qiniu.com,只要獲取上傳憑證就可以了,如下:
文件上傳的組件部分
對應的主要方法(這些方法在不同平台的上傳過程中都是可用的,內部具體操作可能會有所不同):
文件上傳相應方法
騰訊雲和七牛雲的上傳方式類似,不同的是上傳地址是通過請求憑證獲取到的,因此組件屬性中的 aciton 字段的需要通過請求獲得:如下:
阿里雲和騰訊雲上傳的不同點在於請求方式和文件格式不同,而 upload 組件默認屬性不支持對應的格式,因此需要自定義上傳行為,具體實現如下:
import React, { PureComponent } from 'react'; import { Upload, Icon, message } from 'antd'; import apis from '@/services/api'; import axios from 'axios'; class Uploader extends PureComponent { state = { key: '', url: '', imageUrl: '', } // 這里可以做上傳之前的操作,比如文件大小的校驗等
beforeUpload = async (file) => { const res = await this.fetchUploadToken(); return res; } // 獲取上傳憑證
fetchUploadToken = async () => { const params = { quantity: 1, module: 3, fileType: 1, }; const res = await apis.fileSign(params); const { d, m } = res; if (m === 'success') { const { key, url } = d.l[0]; this.setState({ key, url }); return true; } else { return false; } } render() { const { imageUrl, url, key } = this.state; const that = this; const uploadProps = { name: 'file', showUploadList: false, multiple: false, accept: '.png, .jpg, .jpeg, .gif', action: url, beforeUpload: that.beforeUpload, // 這里需要指定文件上傳的content-type
headers: { 'Content-Type': 'application/octet-stream', }, // 自定文件上傳的方法,覆蓋組件的 onChange 方法,可以定義上傳不同階段的行為(由 axios 默認提供)
onStart(file) { console.log('onStart', file, file.name); }, onSuccess(ret, file) { console.log('onSuccess', ret, file); that.props.getData(key); }, onProgress({ percent }, file) { console.log('onProgress', `${percent}%`, file.name); }, onError(err) { console.log('onError', err); }, customRequest({ action, file, headers, onError, onProgress, onSuccess, withCredentials, }) { // 使用 FileReader 將上傳的文件轉換成二進制流,滿足 'application/octet-stream' 格式的要求
const reader = new FileReader(); reader.readAsArrayBuffer(file); let fileData = null; reader.onload = (e) => { // 在文件讀取結束后執行的操作
fileData = e.target.result; // 使用 axios 進行文件上傳的請求
axios.put(action, fileData, { withCredentials, headers, onUploadProgress: ({ total, loaded }) => { // 進行上傳進度輸出,更加直觀
onProgress({ percent: Math.round(loaded / total * 100).toFixed(2) }, file); }, }).then(response => { onSuccess(response, file); }) .catch(onError); }; return { abort() { console.log('upload progress is aborted.'); }, }; }, }; return ( <div>
<Upload {...uploadProps}> { imageUrl ? <img src={imageUrl} /> : <Icon type='plus' /> } </Upload>
</div>
); } } export default Uploader;
使用如上 PUT 請求上傳文件,在瀏覽器中打印信息格式如下:
總結:圖片上傳一直是前端令人頭疼的問題,不同的服務器平台對請求方式和文件格式可能有不同的要求,因此在上傳之前需要做對應的文件處理,而且因為環境不同,還需要和后端合作處理跨域的問題,盡管很多優秀的組件已經提供了響應的處理方法,但是如果對組件實現原理和api不夠了解,可能依舊無法實現一些具體的功能,所以在實現文件上傳的時候,需要多研究,多總結,針對遇到的問題要及時記錄,避免再次踩坑。
【參考資料】:
https://github.com/react-component/upload/blob/master/examples/customRequest.js
vue前端上傳文件到阿里雲oss的兩種方式,put文件流上傳,multipartUpload直接上傳
FileReader - Web API 接口參考 | MDN