基于Jquery实现服务器分块下载数据并追加至客户端文件
客户端分块追加写入CSV文件方案
我将数十万行JSON格式数据存储在Postgres服务器,需要实现分块下载功能:循环调用API拉取小批量数据,收到响应后立即追加到同一个CSV文件,避免在客户端缓存全量数据导致内存占用过高。当前代码每次调用都会生成新文件,无法实现追加写入。
原代码如下:
const writeDataToFile = (data) => { const rows = data.map((obj) => Object.values(obj).join(delimiter)); const csvContent = rows.join('\n'); const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); if (navigator.msSaveBlob) { navigator.msSaveBlob(blob, filename); } else { const link = document.createElement('a'); if (link.download !== undefined) { const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', filename); link.setAttribute('target', '_blank'); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } } };
解决方案
现代浏览器方案(推荐):使用FileSystemAccess API
该API支持直接在用户选择的本地文件中追加内容,无需缓存全量数据,内存占用极低,适用于Chrome、Edge等现代浏览器。
- 获取文件句柄
async function getFileHandle() { const options = { types: [ { description: 'CSV文件', accept: { 'text/csv': ['.csv'], }, }, ], }; const handle = await window.showSaveFilePicker(options); return handle; }
- 实现追加写入逻辑
async function appendToFile(handle, content, isFirstWrite = false) { const writable = await handle.createWritable(); // 首次写入时添加表头 if (isFirstWrite) { const headers = Object.keys(content[0]).join(','); await writable.write(headers + '\n'); } // 转换数据为CSV行并追加 const rows = content.map(obj => Object.values(obj).join(',')).join('\n'); await writable.write(rows + '\n'); await writable.close(); }
- 分块下载主流程
async function downloadChunks(apiUrlTemplate, totalChunks) { const fileHandle = await getFileHandle(); let isFirstWrite = true; for (let i = 0; i < totalChunks; i++) { // 替换为你的分页API请求逻辑 const response = await fetch(`${apiUrlTemplate}?page=${i+1}&pageSize=1000`); const data = await response.json(); await appendToFile(fileHandle, data, isFirstWrite); isFirstWrite = false; // 可添加进度提示 console.log(`已完成第${i+1}/${totalChunks}块数据写入`); } alert('文件下载完成'); }
旧浏览器兼容方案:增量构建Blob
如果需要兼容不支持FileSystemAccess API的旧浏览器,可以采用增量构建Blob的方式(仍会占用一定内存,但比全量缓存小):
let blobParts = []; let isFirstWrite = true; async function downloadChunksLegacy(apiUrlTemplate, totalChunks) { for (let i = 0; i < totalChunks; i++) { const response = await fetch(`${apiUrlTemplate}?page=${i+1}&pageSize=1000`); const data = await response.json(); let csvContent = ''; // 首次写入添加表头 if (isFirstWrite) { const headers = Object.keys(data[0]).join(','); csvContent += headers + '\n'; isFirstWrite = false; } const rows = data.map(obj => Object.values(obj).join(',')).join('\n'); csvContent += rows + '\n'; blobParts.push(new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })); console.log(`已完成第${i+1}/${totalChunks}块数据处理`); } // 合并Blob并生成下载链接 const finalBlob = new Blob(blobParts, { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(finalBlob); const link = document.createElement('a'); link.href = url; link.download = 'data.csv'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }
内容的提问来源于stack exchange,提问作者puneet
相关产品推荐
相关产品推荐

