如何通过Qlik Sense API直接导出指定工作表PDF,无需点击下载链接?
Qlik Sense API 自动导出并下载工作表PDF(无需手动点击)
问题背景
目前通过Qlik Sense API,可依据应用ID(appId)和工作表ID(sheetId)导出PDF,但导出后必须手动点击链接才能触发下载,无法跳过该操作。需求是通过API直接获取指定工作表的PDF文件并以附件形式自动下载,无需手动交互。
解决方案
原代码中模拟点击隐藏链接的方式可能被部分浏览器拦截,我们可以通过Fetch API获取PDF的Blob对象再触发下载,这种方式兼容性更强,能有效避免浏览器拦截问题。修改后的完整代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Qlik Sense 自动导出PDF</title> <meta charset="utf-8"> <script src="https://pa-qliksense.aviv.gov.il/sense/app/appId"></script> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="HandheldFriendly" content="True"> <meta name="MobileOptimized" content="320"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta http-equiv="cleartype" content="on"> <script src="../../resources/assets/external/requirejs/require.js"></script> <style> /* 加载动画CSS */ .spinner { border: 16px solid #f3f3f3; border-radius: 50%; border-top: 16px solid #3498db; width: 120px; height: 120px; animation: spin 2s linear infinite; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #spinnerContainer { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.8); z-index: 9999; display: flex; align-items: center; justify-content: center; } #spinnerContainer.hidden { display: none; } </style> </head> <body style="overflow: auto"> <div id="spinnerContainer"> <div class="spinner"></div> </div> <div id="CurrentSelections" class="qvobjects" style="position:relative; top:0; left:0; width:100%; height:38px;"></div> <div id="downloadLinks" style="margin-top: 20px;"></div> <!-- 页面加载时自动触发导出 --> <script> window.onload = function () { var urlParams = new URLSearchParams(window.location.search); var appId = urlParams.get('appId'); var sheetId = urlParams.get('sheetId'); // 批量整理筛选参数,过滤空值 const fieldParams = Array.from({length:8}, (_,i) => ({ name: urlParams.get(`fieldName${i+1}`), value: urlParams.get(`fieldValue${i+1}`) })).filter(item => item.name && item.value); exportData(appId, sheetId, fieldParams); }; function exportData(appId, sheetId, fieldParams) { var prefix = window.location.pathname.substr(0, window.location.pathname.toLowerCase().lastIndexOf("/extensions") + 1); var config = { host: window.location.hostname, prefix: prefix, port: window.location.port, isSecure: window.location.protocol === "https:" }; require.config({ baseUrl: (config.isSecure ? "https://" : "http://") + config.host + (config.port ? ":" + config.port : "") + config.prefix + "resources" }); require(["js/qlik"], function (qlik) { qlik.setOnError(function (error) { console.error('错误:', error); alert('错误: ' + error.message); }); qlik.theme.apply('horizon'); // 打开目标应用 var app = qlik.openApp(appId, config); // 清除原有筛选后应用新条件 app.clearAll().then(function () { // 批量执行筛选操作,确保所有条件生效后再导出 Promise.all(fieldParams.map(param => app.field(param.name).selectValues([param.value], true, false) )).then(() => { // 获取工作表对象并导出PDF app.visualization.get(sheetId).then(function (object) { object.exportPdf({ documentSize: 'A4', aspectRatio: 0, orientation: "landscape", objectSize: { height: 1280, width: 1920 } }).then(function (pdfExportUrl) { // 通过Fetch获取PDF文件Blob,自动触发下载 fetch(pdfExportUrl, { credentials: 'include' }) .then(response => response.blob()) .then(blob => { const tempUrl = window.URL.createObjectURL(blob); const downloadLink = document.createElement('a'); downloadLink.href = tempUrl; downloadLink.download = '导出的仪表盘.pdf'; document.body.appendChild(downloadLink); downloadLink.click(); // 清理临时资源 window.URL.revokeObjectURL(tempUrl); document.body.removeChild(downloadLink); // 隐藏加载动画 document.getElementById('spinnerContainer').classList.add('hidden'); }) .catch(fetchError => { console.error('获取PDF文件失败:', fetchError); alert('获取PDF文件失败,请查看控制台详情'); document.getElementById('spinnerContainer').classList.add('hidden'); }); }).catch(function (pdfError) { console.error('导出PDF失败:', pdfError); alert('导出PDF失败,请查看控制台详情'); document.getElementById('spinnerContainer').classList.add('hidden'); }); }).catch(function (error) { console.error('获取工作表对象失败:', error); alert('获取工作表对象失败,请查看控制台详情'); document.getElementById('spinnerContainer').classList.add('hidden'); }); }).catch(selectError => { console.error('应用筛选条件失败:', selectError); alert('应用筛选条件失败,请查看控制台详情'); document.getElementById('spinnerContainer').classList.add('hidden'); }); }).catch(function (error) { console.error('清除筛选失败:', error); alert('清除筛选失败,请查看控制台详情'); document.getElementById('spinnerContainer').classList.add('hidden'); }); }); } </script> </body> </html>
关键修改点
- 筛选参数优化:将原有的8个单独参数改为批量处理,过滤空值后统一应用,代码更简洁易维护
- 下载逻辑升级:使用Fetch API获取PDF的Blob对象,通过临时URL触发下载,避免浏览器拦截非用户交互的点击事件
- 权限保障:Fetch请求中加入
credentials: 'include',确保Qlik Sense的会话权限正常传递 - 异步流程优化:用
Promise.all确保所有筛选条件生效后再执行导出,避免数据不一致问题
注意事项
- 确保当前页面与Qlik Sense服务器同源,或服务器已配置跨域允许规则
- 部分严格模式浏览器可能仍会限制自动下载,内部系统环境下该方案可稳定运行;公网环境可添加一个初始触发按钮,由用户主动点击后启动导出流程
内容的提问来源于stack exchange,提问作者Neomi
相关产品推荐
相关产品推荐

