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

NodeJS+AngularJS下载文件损坏乱码问题求助

下载二进制文件(xlsx/zip)损坏乱码问题排查与修复

问题现象

通过NodeJS+Express构建的API下载xlsx、zip格式文件时,文件损坏无法正常打开,部分xlsx文件打开显示乱码(示例:��Ӫ��E���Q^���3�3�ϾhwR-��vY!c�O�y�����I�㱣�eG������[J�Gߌw������ES��lz�e�L�'Sr��`x2�R��Į��¦)。

核心问题根源

二进制文件被错误地当成JSON文本进行传输和解析,导致原始二进制数据的编码信息被破坏,最终出现文件损坏或乱码。


后端代码问题分析与修复

原代码问题点

  1. 将文件二进制Buffer包装在JSON响应中返回,二进制数据被序列化为JSON数字数组,丢失了原始二进制的编码完整性。
  2. 未设置正确的响应头(如Content-Type、Content-Disposition),无法告知浏览器这是一个需要下载的二进制文件。

修复后的后端代码

public async downloadFile(patch: string, start: [number, number], timeout: number) {
    try {
        // ... 省略storageInfo获取逻辑
        const file = this.bucket.file(storageInfo.$path);
        
        // 获取文件元信息,用于设置正确的Content-Type
        const [metadata] = await file.getMetadata();
        const contentType = metadata.contentType || 'application/octet-stream';

        return new Promise((resolve, reject) => {
            const readStream = file.createReadStream();
            
            readStream.on('error', (err) => {
                readStream.destroy();
                reject(err);
            });
            
            // 设置响应头,标识文件类型和下载信息
            readStream.setHeader('Content-Type', contentType);
            readStream.setHeader(
                'Content-Disposition', 
                `attachment; filename="${encodeURIComponent(storageInfo.fileName)}"`
            );
            
            resolve(readStream);
        });
    } catch (ex) {
        // ... 错误处理逻辑
        throw ex;
    }
}

说明:如果需要同时返回文件元信息,可将信息放在自定义响应头(如X-File-Info)中,避免嵌套在响应体破坏二进制数据。


前端代码问题分析与修复

原代码问题点

  1. 使用TextDecoder将二进制arraybuffer解码为文本后再做JSON.parse,这会把非UTF-8编码的二进制文件(xlsx/zip)当成文本解析,直接破坏原始数据。
  2. 从JSON中取出的数组转Uint8Array时,数据已经是损坏状态,后续生成的Blob自然无法正常打开。

修复后的前端代码

public async previewFileFromGoogleStorage(hash: string): Promise<IFileDownload> {
    try {
        if (!hash) return null;

        const response: angular.IHttpResponse<ArrayBuffer> = await this.$http({
            method: 'GET',
            url: `${this.getUrlDownload()}${hash}`,
            cache: false,
            timeout: this.timeout,
            responseType: "arraybuffer"
        });
        
        // 从响应头解析文件名(后端已设置Content-Disposition)
        const contentDisposition = response.headers('Content-Disposition');
        let fileName = 'download';
        if (contentDisposition) {
            const matches = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
            fileName = matches ? decodeURIComponent(matches[1].replace(/['"]/g, '')) : fileName;
        }
        
        // 从响应头获取文件类型
        const fileType = response.headers('Content-Type') || this.getContentType('');
        
        // 直接用原始arraybuffer生成Blob
        const fileBlob = new Blob([response.data], { type: fileType });
        const fileURL = window.URL.createObjectURL(fileBlob);
        
        return { url: fileURL, name: fileName, type: fileType, blob: fileBlob };

    } catch (ex) {
        HandleError.exception(ex);
    }
}

private downloadFile(): void {
    if (this.$rootScope.isIE) {
        window.navigator.msSaveOrOpenBlob(this.$scope.file.blob, this.$scope.file.name);
    } else {
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(this.$scope.file.blob);
        link.download = this.$scope.file.name;
        link.click();
        // 释放URL资源,避免内存泄漏
        window.URL.revokeObjectURL(link.href);
    }
}

内容的提问来源于stack exchange,提问作者azh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:35:38