You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt3项目中PDF转CSV转换器生成Blob/ObjectURL失败,输出无效JSON文件问题求助

Nuxt3项目中PDF转CSV转换器生成Blob/ObjectURL失败,输出无效JSON文件问题求助

我最近在开发一个PDF转CSV的转换器,现在卡在最后一步了——代码能正常执行,但生成下载链接时,本该生成Blob对应的ObjectURL,结果却输出了一个包含原始数据的无效JSON文件,完全没法正常使用。我自己折腾了好长时间,甚至找AI帮忙排查,都没解决问题。AI提示问题可能出在Composable/useConvert.ts文件里,我也觉得是这个文件的锅,因为它负责把API返回的转换结果处理成正确格式,再生成供客户端下载的链接。有没有大佬能帮我看看啊?


类型声明文件

types/new-parsers.d.ts

declare module 'pdf-parse/lib/pdf-parse.js' {
  interface PDFParse {
    (dataBuffer: Buffer, options?: any): Promise<{
      numpages: number;
      numrender: number;
      info: any;
      metadata: any;
      version: string;
      text: string;
    }>;
  }
  const pdf: PDFParse;
  export default pdf;
}

declare module 'papaparse' {
  // 这里可以根据需求补充更详细的类型,不过目前用any足够处理我们的动态数据
  export function parse(csvString: string, config: any): { data: any[], errors: any[], meta: any };
  export function unparse(data: any[] | object, config?: any): string;
}

CSV转换核心代码

server/utils/csvConverter.ts

import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs/promises';
import libre from 'libreoffice-convert';
import { promisify } from 'util';
import { load } from 'cheerio'; // 命名导入
import pdf from 'pdf-parse/lib/pdf-parse.js';
import Papa from 'papaparse';
import { XMLParser, XMLBuilder } from 'fast-xml-parser';

const libreConvertAsync = promisify(libre.convert);

interface MultiPartData {
  data: Buffer;
  filename?: string;
}

// --- 新的健壮辅助函数 ---
async function convertWithLibreOffice(inputBuffer: Buffer, outputExtension: string, tempDir: string): Promise<Buffer> {
  try {
    const inputPath = path.join(tempDir, `inputfile${path.extname(tempDir)}`); // 给文件加个扩展名
    await fs.writeFile(inputPath, inputBuffer);
    return await libreConvertAsync(inputBuffer, `.${outputExtension}`, undefined);
  } catch (err: any) {
    console.error("LibreOffice转换失败,请确认LibreOffice已安装并添加到系统PATH中?", err);
    // 抛出更友好的错误信息给用户
    throw new Error("文件转换失败,请确保服务器已安装LibreOffice。");
  }
}

export async function handleCsvConversion(file: MultiPartData, targetFormat: string): Promise<{ buffer: Buffer; filename: string; contentType: string }> {
  const extension = path.extname(file.filename!).slice(1).toLowerCase();
  const newFilename = file.filename!.replace(/\.[^/.]+$/, `.${targetFormat}`);
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nuxt-csv-'));

  try {
    let outputBuffer: Buffer;
    let contentType = 'text/csv';

    if (targetFormat === 'csv') {
      if (['xlsx', 'ods'].includes(extension)) {
        outputBuffer = await convertWithLibreOffice(file.data, 'csv', tempDir);
      } else if (extension === 'pdf') {
        const data = await pdf(file.data);
        const csvContent = `"${data.text.replace(/"/g, '""')}"`;
        outputBuffer = Buffer.from(csvContent, 'utf-8');
      } else if (extension === 'html') {
        const $ = load(file.data.toString());
        const rows: string[][] = [];
        $('table tr').each((i, row) => {
          const rowData: string[] = [];
          $(row).find('th, td').each((j, cell) => {
            rowData.push(`"${$(cell).text().trim().replace(/"/g, '""')}"`);
          });
          rows.push(rowData);
        });
        outputBuffer = Buffer.from(rows.map(row => row.join(',')).join('\n'));
      } else if (extension === 'json' || extension === 'bson') {
        const jsonData = JSON.parse(file.data.toString());
        const csvString = Papa.unparse(jsonData);
        outputBuffer = Buffer.from(csvString);
      } else if (extension === 'xml') {
        const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "" });
        const jsonObj = parser.parse(file.data);
        const arrayToConvert = Object.values(jsonObj[Object.keys(jsonObj)[0]]).find(Array.isArray);
        if (!arrayToConvert) throw new Error('无法在XML中找到清晰的数组结构用于转换为CSV。');
        const csvString = Papa.unparse(arrayToConvert);
        outputBuffer = Buffer.from(csvString);
      } else if (extension === 'srt') {
        const srtContent = file.data.toString();
        const entries = srtContent.split(/\r?\n\r?\n/);
        const csvRows = [["index", "start", "end", "text"]];
        for (const entry of entries) {
          if (entry.trim() === '') continue;
          const lines = entry.split(/\r?\n/);
          csvRows.push([
            `"${lines[0]}"`,
            `"${lines[1].split(' --> ')[0]}"`,
            `"${lines[1].split(' --> ')[1]}"`,
            `"${lines.slice(2).join(' ').replace(/"/g, '""')}"`
          ]);
        }
        outputBuffer = Buffer.from(csvRows.map(row => row.join(',')).join('\n'));
      }
    }

    return { buffer: outputBuffer, filename: newFilename, contentType };
  } finally {
    // 清理临时目录
    await fs.rm(tempDir, { recursive: true, force: true });
  }
}

目前我怀疑问题出在前端的useConvert.ts组合式函数里,它负责接收后端返回的转换结果并生成Blob URL供用户下载。但现在后端返回的buffer数据,前端可能没有正确处理成Blob,反而直接把原始数据序列化成了JSON,导致下载的文件不对。有没有大佬能帮我排查下,到底是后端转换逻辑有问题,还是前端处理Blob的代码写错了?感激不尽!


内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 09:49:34