使用FileReader结合Recruiterbox API时encoded_data返回undefined求助
搞定FileReader异步问题,让Recruiterbox API拿到正确的encoded_data
嘿,我帮你捋清楚这个问题:你遇到的encoded_data返回undefined的核心原因,就是没正确处理FileReader的异步特性——文件读取是异步操作,你之前的代码要么直接拿了异步方法的返回值(本来就是undefined),要么没等读取完成就把字段塞进数组提交了。
先拆解你之前代码的问题
- 初始代码:
getBase64里直接返回reader.readAsDataURL(file),但这个方法本身就返回undefined,而且onloadend回调里的reader.result根本传不出来——异步回调的返回值没法直接给同步代码用。 - 更新1代码:虽然用了Promise封装,但for循环里调用
getBase64().then()的时候,没等Promise完成就把obj推去fields了。这时候obj.value还没被赋值,提交自然是空的。 - 更新2代码:用了async/await是对的,但有两个小坑:一是用了
var i的普通for循环(虽然await在这里能工作,但for...of更稳妥,避免变量作用域问题);二是不确定app.postApplyOpening是不是异步方法,如果不是的话,可能还是会提前提交。
给你调整好的可运行代码
我把代码改了下,确保所有文件读取完成后再构建完整的fields数组,然后才调用API:
applyOpening: async function applyOpening() { // 封装getBase64,直接返回去掉前缀的纯base64字符串 function getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { // 去掉dataURL的前缀,只留纯base64编码(Recruiterbox应该要这个) const pureBase64 = reader.result.replace(/^data:[A-Za-z]+\/[A-Za-z]+;base64,/, ''); resolve(pureBase64); }; reader.onerror = reject; // 别忘了处理读取错误 reader.readAsDataURL(file); }); } const fields = []; const formControls = document.querySelectorAll('.form-control'); // 用for...of循环配合await,确保每个文件读完再处理下一个 for (const field of formControls) { const fieldName = field.getAttribute('name'); if (!fieldName) continue; // 跳过没有name属性的无效字段 const fieldObj = { key: fieldName }; if (field.tagName.toLowerCase() === 'input' && field.getAttribute('type') === 'file') { const selectedFile = field.files[0]; if (selectedFile) { try { const encodedData = await getBase64(selectedFile); // 去掉浏览器自带的fake路径,只留真实文件名 const cleanFileName = field.value.replace(/C:\\fakepath\\/g, ''); fieldObj.value = { encoded_data: encodedData, file_name: cleanFileName }; } catch (err) { console.error('读取文件出错:', err); // 出错时可以设置默认值或者跳过,这里设为null fieldObj.value = null; } } else { fieldObj.value = null; // 没选文件的情况 } } else { fieldObj.value = field.value; } fields.push(fieldObj); console.log('当前处理的字段:', fieldObj); } // 确保所有字段都处理完,再调用提交方法 // 注意:如果postApplyOpening不是异步的,要把它改成返回Promise的形式 await app.postApplyOpening(fields); },
关键改进的地方
- Promise封装更实用:
getBase64直接返回处理好的纯base64字符串,不用后续再处理前缀,还加了错误捕获; - 异步时序完全可控:
for...of+await确保每个文件读取完成后再处理下一个字段,fields数组里的每个文件字段都有正确的encoded_data; - 代码更健壮:跳过无效字段、处理未选文件的场景、捕获读取错误,不会因为一个文件出问题就整个流程挂掉;
- 提交时机正确:等所有字段都处理完才调用
postApplyOpening,不会提前提交不完整的数据。
另外,如果你原来的app.postApplyOpening是用jQuery AJAX写的,得把它改成返回Promise的形式,这样await才能生效:
postApplyOpening: function(fields) { return new Promise((resolve, reject) => { $.ajax({ url: `https://jsapi.recruiterbox.com/v1/openings/${id}/apply?client_name=clientname`, data: JSON.stringify({ fields }), dataType: 'json', contentType: 'application/json', type: 'POST', success: (response) => resolve(response), error: (err) => reject(err) }); }); }
这样改完之后,应该就能让Recruiterbox API拿到正确的encoded_data了。
内容的提问来源于stack exchange,提问作者jpcmf
相关产品推荐
相关产品推荐

