最近項目中遇到一個問題,咱們須要在cocos項目裏去上傳音頻文件,而cocos原生環境和平時咱們開發所在的瀏覽器環境和Node環境有不少差別,而cocos環境只提供了基礎類,沒有提供FormData這種封裝類。ios
因此問題來了?如何實現一個FormData,以及怎麼去使用它?ajax
這裏我列一個最簡單的例子,咱們來看看FormData究竟是什麼。chrome
function App() { const [name, setName] = useState('') const [age, setAge] = useState(0) const [file, setFile] = useState<File | null>() const submit = () => { console.log(name, age); console.log(file); var fd = new FormData() fd.append('name', name) fd.append('age', age.toString()) fd.append('file', file as Blob) $.ajax({ type: "POST", url: "www.happy.com", data: fd, processData: false,//重要 contentType: 'multipart/form-data',//重要 success: function (data: any) { } }) } return ( <div className="App"> <form action="form_action.asp" method="get"> <p>name: <input type="text" name="name" value={name} onChange={e => setName(e.currentTarget.value)} /></p> <p>age: <input type="number" name="age" value={age} onChange={e => setAge(Number(e.currentTarget.value))} /></p> <p>file:<input type="file" name="file" onChange={e => setFile(e.target.files && e.target.files[0])}/> </p> <input type="button" name="b1" value="submit" onClick={() => submit()} /> </form> </div> ); }
FormData:FormData 接口提供了一種表示表單數據的鍵值對 key-value 的構造方式,而且能夠輕鬆的將數據經過XMLHttpRequest.send() 方法發送出去,本接口和此方法都至關簡單直接。若是送出時的編碼類型被設爲 "multipart/form-data",它會使用和表單同樣的格式。(摘自MDN)axios
大多數文章裏,只給了這樣的一種描述或者說是概念,它是一個接口類,用來作上傳用,咱們來看它在數據形式上體現的是什麼。後端
下面是chrome devtool request payload裏的樣子。api
------WebKitFormBoundaryuhGsgTdqAAltAXy7 // 分隔/邊界符 Content-Disposition: form-data; name="name" // 內聯形式 hackftz // value ------WebKitFormBoundaryuhGsgTdqAAltAXy7 Content-Disposition: form-data; name="age" 22 ------WebKitFormBoundaryuhGsgTdqAAltAXy7 Content-Disposition: form-data; name="file"; filename="Minstrel - eyecatch!.mp3" Content-Type: audio/mpeg ------WebKitFormBoundaryuhGsgTdqAAltAXy7-- // 這裏是end_boundary,結尾分隔/邊界符,必需!
先貼代碼,而後說說我遇到了哪些坑。數組
export default class MyFormData { // 將隨機數傳入構造函數 constructor(stamp) { this._boundary_key = stamp; // 隨機數,分隔符和結尾分隔符必需。 this._boundary = '--' + this._boundary_key; this._end_boundary = this._boundary + '--'; this._result = []; } // 上傳普通鍵值對——字符串、數字 append(key, value) { this._result.push(this._boundary + '\r\n'); this._result.push('Content-Disposition: form-data; name="' + key + '"' + '\r\n\r\n'); this._result.push(value); this._result.push('\r\n'); } // 上傳複雜數據——文件 appendFile(name, data, ext){ let type = "audio/mpeg"; let filename = "upload."+ext; this._result.push(`${this._boundary}\r\n`); this._result.push(`Content-Disposition: form-data; name="${name}"; filename="${filename}"\r\n`); // 上傳文件定義 this._result.push(`Content-Type: ${type}\r\n`); this._result.push("\r\n"); this._result.push(data); this._result.push("\r\n"); } // 獲取二進制數據 get get arrayBuffer() { this._result.push('\r\n' + this._end_boundary); // 結尾分隔符 let charArr = []; // 處理charCode for (let i = 0; i < this._result.length; i++) { // 取出文本的charCode(10進制 let item = this._result[i]; if( typeof(item) === 'string'){ for (let s = 0; s < item.length; s++){ charArr.push(item.charCodeAt(s)); } } else if(typeof(item) === 'number') { let numstr = item.toString() for (let s = 0; s < numstr.length; s++){ charArr.push(numstr.charCodeAt(s)); } } else{ for (let j = 0; j < item.length; j++){ charArr.push(item[j]); } } } let array = new Uint8Array(charArr); return array.buffer; } }
踩坑記錄:瀏覽器
以上是封裝FormData中我遇到的問題,再來看怎麼去使用這樣一個咱們自定義的FormData。服務器
話很少說,先貼代碼,再談問題:app
const stamp = Date.now() // 生成隨機數,這裏使用了時間戳 const fd = new MyFormData(stamp) for (const key in data) { if (data.hasOwnProperty(key)) { fd.append(key, data[key]) } } fd.appendFile('file', blob, data.fileExtName); // 添加要上傳的文件,這裏記得第三個參數要傳入文件後綴名。 const config = { headers: { 'Content-Type': `multipart/form-data; boundary=${stamp}` // 分隔符 }, }; axios({ url, data: fd.arrayBuffer, method: 'POST', headers }) .then(response => { if (response.status === 200) { const { data } = response; console.log("fun -> JSON.stringify(data)", JSON.stringify(data)) } }) .catch(err => { console.log(err); });
踩坑記錄:
multipart/form-data; boundary=${stamp}
這裏必定要把隨機數寫到boundary=
後面,不然後端服務會報錯'no multipart boundary was found'