最近项目中遇到一个问题,咱们须要在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'