From 71638d4bf9a7b28ef79b3def15dc076bef3fe61c Mon Sep 17 00:00:00 2001 From: mertmit Date: Mon, 25 Nov 2024 16:32:53 +0000 Subject: [PATCH] feat: encoding for export stream --- .../src/controllers/attachments.controller.ts | 13 +++++++++++++ packages/nocodb/src/models/PresignedUrl.ts | 10 ++++++++++ .../jobs/jobs/data-export/data-export.processor.ts | 14 +++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/controllers/attachments.controller.ts b/packages/nocodb/src/controllers/attachments.controller.ts index efbb55ead9..8efb3358ef 100644 --- a/packages/nocodb/src/controllers/attachments.controller.ts +++ b/packages/nocodb/src/controllers/attachments.controller.ts @@ -168,6 +168,7 @@ export class AttachmentsController { let queryResponseContentType = null; let queryResponseContentDisposition = null; + let queryResponseContentEncoding = null; if (queryHelper.length > 1) { const query = new URLSearchParams(queryHelper[1]); @@ -175,6 +176,7 @@ export class AttachmentsController { queryResponseContentDisposition = query.get( 'ResponseContentDisposition', ); + queryResponseContentEncoding = query.get('ResponseContentEncoding'); } const targetParam = param.split('/')[2]; @@ -191,12 +193,23 @@ export class AttachmentsController { if (queryResponseContentType) { res.setHeader('Content-Type', queryResponseContentType); + + if (queryResponseContentEncoding) { + res.setHeader( + 'Content-Type', + `${queryResponseContentType}; charset=${queryResponseContentEncoding}`, + ); + } } if (queryResponseContentDisposition) { res.setHeader('Content-Disposition', queryResponseContentDisposition); } + if (queryResponseContentEncoding) { + res.setHeader('Content-Encoding', queryResponseContentEncoding); + } + res.sendFile(file.path); } catch (e) { res.status(404).send('Not found'); diff --git a/packages/nocodb/src/models/PresignedUrl.ts b/packages/nocodb/src/models/PresignedUrl.ts index 7829be6067..d8fd75e2bc 100644 --- a/packages/nocodb/src/models/PresignedUrl.ts +++ b/packages/nocodb/src/models/PresignedUrl.ts @@ -96,6 +96,7 @@ export default class PresignedUrl { filename?: string; preview?: boolean; mimetype?: string; + encoding?: string; }, ncMeta = Noco.ncMeta, ) { @@ -109,6 +110,7 @@ export default class PresignedUrl { expireSeconds = DEFAULT_EXPIRE_SECONDS, filename, mimetype, + encoding, } = param; const preview = param.preview @@ -152,6 +154,14 @@ export default class PresignedUrl { if (mimetype) { pathParameters.ResponseContentType = mimetype; + + if (encoding) { + pathParameters.ResponseContentType = `${mimetype}; charset=${encoding}`; + } + } + + if (encoding) { + pathParameters.ResponseContentEncoding = encoding; } // append query params to the cache path diff --git a/packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts b/packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts index 61b51104ea..dfd3f1511e 100644 --- a/packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts +++ b/packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts @@ -1,5 +1,6 @@ import { Readable } from 'stream'; import path from 'path'; +import iconv from 'iconv-lite'; import { Injectable, Logger } from '@nestjs/common'; import moment from 'moment'; import type { Job } from 'bull'; @@ -61,10 +62,19 @@ export class DataExportProcessor { dataStream.setEncoding('utf8'); + const encodedStream = + options?.encoding && + options.encoding !== 'utf-8' && + iconv.encodingExists(options.encoding) + ? dataStream + .pipe(iconv.decodeStream('utf-8')) + .pipe(iconv.encodeStream(options?.encoding || 'utf-8')) + : dataStream; + let error = null; const uploadFilePromise = (storageAdapter as any) - .fileCreateByStream(destPath, dataStream) + .fileCreateByStream(destPath, encodedStream) .catch((e) => { this.logger.error(e); error = e; @@ -97,6 +107,7 @@ export class DataExportProcessor { expireSeconds: 3 * 60 * 60, // 3 hours preview: false, mimetype: 'text/csv', + encoding: options?.encoding || 'utf-8', }); } else { url = await PresignedUrl.getSignedUrl({ @@ -105,6 +116,7 @@ export class DataExportProcessor { expireSeconds: 3 * 60 * 60, // 3 hours preview: false, mimetype: 'text/csv', + encoding: options?.encoding || 'utf-8', }); }