html2pdf.js异步批量生成PDF后结果展示异常求助
批量异步生成PDF后展示文件列表的问题修复
我正在尝试用html2pdf.js以异步方式批量生成PDF文件,目前PDF生成功能正常,但希望所有文件生成完成后,在div.alert元素中展示生成的文件列表及其访问路径,并隐藏加载状态。我写了如下代码但存在问题,恳请帮忙排查修复:
var envLabSub = ['env1', 'env2']; //call function to convert in PDF convertToPdf(msg, subject); function convertToPdf(msg, subject) { envLabSub.forEach(async env => { await printer(msg, subject, env); }) var message = "<h3><span class='glyphicon glyphicon-warning-sign' aria-hidden='true'></span> Email was sent with success !!</h3>"; $("div.alert").removeClass().addClass('alert alert-success').html(message); $("div.alert").append("<ul></ul>"); returArray.forEach(function (pdf) { $("div.alert ul").append("<li>File :<b>" + pdf.fileName + "</b> was created with success<br><a href='" + pdf.path + "' target='_blank'>" + pdf.path + "</a></ii>"); }); $("div.alert").show(); $(".loading").hide(); } async function printer(msg, subject, env) { var divToPrint = $('<div/>').append(msg); var versionTarget = $('#inpTargetVersion').val(); var service = $('#Select_Service option:selected').text(); var returArray = []; // date export Csv var MyDate = new Date(); var datePdf = MyDate.getFullYear() + "-" + ('0' + (MyDate.getMonth()+1)).slice(-2) + "-" + ('0' + MyDate.getDate()).slice(-2) + "_" + ('0' + MyDate.getHours()).slice(-2) + ('0' + MyDate.getMinutes()).slice(-2) + ('0' + MyDate.getSeconds()).slice(-2); var fileName = datePdf + "_" + env.replace('/','_').trim() + "_" + service.trim() + "_" + versionTarget + ".pdf"; var worker = html2pdf(); await worker.from(divToPrint[0]); let myPdf = await worker.outputPdf('datauristring', 'mypdf.pdf'); //this converts the uri to blob var preBlob = dataURItoBlob(myPdf); var file = new File([preBlob], fileName, {type: 'application/pdf'}); // sends the message to api sendFile(file); } async function sendFile(userFiles) { let filePaths; let formData = new FormData(); formData.append("filePdf", userFiles); let response = await fetch('views/ajax_response.php', { method: 'POST', body: formData }); filePaths = await response.json(); return filePaths; } function dataURItoBlob(dataURI) { // convert base64/URLEncoded data component to raw binary data held in a string var byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) byteString = atob(dataURI.split(',')[1]); else byteString = unescape(dataURI.split(',')[1]); // separate out the mime component var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // write the bytes of the string to a typed array var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], {type:mimeString}); }
问题排查
- forEach异步无效:
forEach不会等待内部异步函数执行完成,导致UI更新代码在所有PDF生成前就已运行,此时没有文件数据。 - 变量作用域错误:
printer内的returArray是局部变量,外部convertToPdf无法访问;且未收集sendFile返回的路径信息。 - 未返回异步结果:
printer函数调用sendFile后未返回结果,无法收集每个文件的路径。 - UI更新时机错误:原代码未等待所有异步任务完成就更新UI,导致列表为空。
修复后的代码
var envLabSub = ['env1', 'env2']; // 调用PDF转换函数 convertToPdf(msg, subject); // 改为异步函数,等待所有任务完成 async function convertToPdf(msg, subject) { // 用Promise.all等待所有printer任务完成,收集结果 const fileResults = await Promise.all( envLabSub.map(env => printer(msg, subject, env)) ); // 更新UI var message = "<h3><span class='glyphicon glyphicon-warning-sign' aria-hidden='true'></span> 所有PDF生成完成 !!</h3>"; $("div.alert").removeClass().addClass('alert alert-success').html(message); const $list = $("<ul></ul>").appendTo("div.alert"); fileResults.forEach(function (pdf) { $list.append(`<li>文件 :<b>${pdf.fileName}</b> 生成成功<br><a href='${pdf.path}' target='_blank'>${pdf.path}</a></li>`); }); $("div.alert").show(); $(".loading").hide(); } async function printer(msg, subject, env) { var divToPrint = $('<div/>').append(msg); var versionTarget = $('#inpTargetVersion').val(); var service = $('#Select_Service option:selected').text(); // 生成文件名 var MyDate = new Date(); var datePdf = MyDate.getFullYear() + "-" + ('0' + (MyDate.getMonth()+1)).slice(-2) + "-" + ('0' + MyDate.getDate()).slice(-2) + "_" + ('0' + MyDate.getHours()).slice(-2) + ('0' + MyDate.getMinutes()).slice(-2) + ('0' + MyDate.getSeconds()).slice(-2); var fileName = datePdf + "_" + env.replace('/','_').trim() + "_" + service.trim() + "_" + versionTarget + ".pdf"; // 生成PDF var worker = html2pdf(); await worker.from(divToPrint[0]); let myPdf = await worker.outputPdf('datauristring', 'mypdf.pdf'); // 转换为Blob并创建File对象 var preBlob = dataURItoBlob(myPdf); var file = new File([preBlob], fileName, {type: 'application/pdf'}); // 上传文件并获取路径,返回文件名和路径的对象 const filePath = await sendFile(file); return { fileName, path: filePath }; } async function sendFile(userFiles) { let formData = new FormData(); formData.append("filePdf", userFiles); let response = await fetch('views/ajax_response.php', { method: 'POST', body: formData }); return await response.json(); } function dataURItoBlob(dataURI) { var byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) byteString = atob(dataURI.split(',')[1]); else byteString = unescape(dataURI.split(',')[1]); var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], {type:mimeString}); }
修复说明
- 将
convertToPdf改为异步函数,使用Promise.all配合map替代forEach,确保所有PDF生成任务完成后再执行UI更新。 printer函数返回包含文件名和路径的对象,收集到fileResults数组中。- 移除无效的局部
returArray变量,改用全局收集任务结果。 - 调整UI更新逻辑,确保在所有文件生成并获取路径后再渲染列表。
内容的提问来源于stack exchange,提问作者Jean-Philippe SANTOS DE JESUS
相关产品推荐
相关产品推荐

