Selaa lähdekoodia

refactor: enhance importExcelTags method for improved CSV parsing and error handling; add logging for better tracking of file processing

Dave 3 viikkoa sitten
vanhempi
säilyke
d83f2acfe2

+ 5 - 4
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.controller.ts

@@ -290,12 +290,13 @@ export class VideoMediaController {
     const reqAny = req as any;
 
     const bodyFile = reqAny.body?.file ?? reqAny.body?.files;
-    let file: MultipartFile | undefined = Array.isArray(bodyFile)
+    const file: MultipartFile | undefined = Array.isArray(bodyFile)
       ? bodyFile[0]
       : bodyFile;
-    if (!file && typeof reqAny.file === 'function') {
-      file = await reqAny.file();
-    }
+
+    this.videoMediaService['logger']?.log?.(
+      `[importExcelTags] body file keys=${file ? Object.keys(file).join(',') : 'none'}`,
+    );
 
     // If this is null, the request is not multipart – nothing else to do
     if (!file) {

+ 60 - 69
apps/box-mgnt-api/src/mgnt-backend/feature/video-media/video-media.service.ts

@@ -7,7 +7,6 @@ import {
 } from '@nestjs/common';
 import type { MultipartFile } from '@fastify/multipart';
 import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
-import ExcelJS from 'exceljs';
 import * as XLSX from 'xlsx';
 import { CacheSyncService } from '../../../cache-sync/cache-sync.service';
 import { CacheEntityType } from '../../../cache-sync/cache-sync.types';
@@ -20,6 +19,7 @@ import {
   UpdateVideoMediaStatusDto,
   BatchUpdateVideoMediaStatusDto,
 } from './video-media.dto';
+import ExcelJS from 'exceljs';
 import { MEDIA_STORAGE_STRATEGY } from '../../../shared/tokens';
 
 type MongoAggregateResult = {
@@ -302,26 +302,38 @@ export class VideoMediaService {
   }
 
   private async readUploadBuffer(file: any): Promise<Buffer> {
-    const toBuffer = (value: unknown): Buffer | undefined => {
-      if (!value) return undefined;
-      if (Buffer.isBuffer(value)) return value;
-      if (value instanceof Uint8Array) return Buffer.from(value);
-      if (Array.isArray(value)) return Buffer.from(value);
-      if (typeof value === 'string') return Buffer.from(value, 'binary');
-      return undefined;
-    };
+    if (Buffer.isBuffer(file)) return file;
+    if (file instanceof Uint8Array) return Buffer.from(file);
+    if (Array.isArray(file)) return Buffer.from(file);
+    if (typeof file === 'string') return Buffer.from(file, 'binary');
+    if (file instanceof ArrayBuffer) {
+      return Buffer.from(new Uint8Array(file));
+    }
+    if (ArrayBuffer.isView(file)) {
+      return Buffer.from(file.buffer, file.byteOffset, file.byteLength);
+    }
 
     if (typeof file?.toBuffer === 'function') {
-      const raw = await file.toBuffer();
-      const buffer = toBuffer(raw);
-      if (buffer) return buffer;
+      return await file.toBuffer();
     }
 
-    const direct = toBuffer(file);
-    if (direct) return direct;
+    if (file?.value && Buffer.isBuffer(file.value)) {
+      return file.value;
+    }
 
-    const fallback = toBuffer(file?.buffer ?? file?.value ?? file?.data);
-    if (fallback) return fallback;
+    if (file?.value && file.value instanceof Uint8Array) {
+      return Buffer.from(file.value);
+    }
+
+    if (file?.value && Array.isArray(file.value)) {
+      return Buffer.from(file.value);
+    }
+
+    const value = file?.value ?? file?.buffer ?? file?.data;
+    if (Buffer.isBuffer(value)) return value;
+    if (value instanceof Uint8Array) return Buffer.from(value);
+    if (Array.isArray(value)) return Buffer.from(value);
+    if (typeof value === 'string') return Buffer.from(value, 'binary');
 
     const stream = file?.file as NodeJS.ReadableStream | undefined;
     if (!stream) {
@@ -346,63 +358,42 @@ export class VideoMediaService {
     );
 
     const rowsAH: Array<{ idRaw: unknown; tagsRaw: unknown }> = [];
-
-    try {
-      const workbook = XLSX.read(buffer, { type: 'buffer' });
-      const sheetName = workbook.SheetNames[0];
-      const sheet = sheetName ? workbook.Sheets[sheetName] : undefined;
-
-      if (!sheet) {
-        throw new BadRequestException('No sheet found in Excel file');
-      }
-
-      const rangeRef = sheet['!ref'] ?? 'A1:A1';
-      const range = XLSX.utils.decode_range(rangeRef);
-
-      for (let r = range.s.r; r <= range.e.r; r += 1) {
-        const idCell = sheet[XLSX.utils.encode_cell({ r, c: 0 })];
-        const tagsCell = sheet[XLSX.utils.encode_cell({ r, c: 7 })];
-        rowsAH.push({
-          idRaw: idCell?.v ?? '',
-          tagsRaw: tagsCell?.v ?? '',
-        });
-      }
-    } catch (error) {
-      const cellToString = (value: unknown): string => {
-        if (value == null) return '';
-        if (typeof value === 'string') return value;
-        if (typeof value === 'number' || typeof value === 'boolean') {
-          return String(value);
-        }
-        if (typeof value === 'object') {
-          const anyValue = value as any;
-          if (typeof anyValue.text === 'string') return anyValue.text;
-          if (Array.isArray(anyValue.richText)) {
-            return anyValue.richText.map((t: any) => t?.text ?? '').join('');
-          }
-          if (typeof anyValue.result !== 'undefined') {
-            return String(anyValue.result);
+    const csvText = buffer.toString('utf8');
+    const lines = csvText.split(/\r?\n/);
+    const parseCsvLine = (line: string): string[] => {
+      const cells: string[] = [];
+      let current = '';
+      let inQuotes = false;
+      for (let i = 0; i < line.length; i += 1) {
+        const ch = line[i];
+        if (ch === '"') {
+          if (inQuotes && line[i + 1] === '"') {
+            current += '"';
+            i += 1;
+          } else {
+            inQuotes = !inQuotes;
           }
+          continue;
         }
-        return String(value);
-      };
-
-      const workbook = new ExcelJS.Workbook();
-      await workbook.xlsx.load(buffer as any);
-      const sheet = workbook.worksheets[0];
-
-      if (!sheet) {
-        throw new BadRequestException('No sheet found in Excel file');
+        if (ch === ',' && !inQuotes) {
+          cells.push(current);
+          current = '';
+          continue;
+        }
+        current += ch;
       }
+      cells.push(current);
+      return cells;
+    };
 
-      const lastRow = sheet.lastRow?.number ?? 0;
-      for (let r = 1; r <= lastRow; r += 1) {
-        const row = sheet.getRow(r);
-        rowsAH.push({
-          idRaw: cellToString(row.getCell(1).value),
-          tagsRaw: cellToString(row.getCell(8).value),
-        });
-      }
+    for (let i = 1; i < lines.length; i += 1) {
+      const line = lines[i];
+      if (!line) continue;
+      const cells = parseCsvLine(line);
+      rowsAH.push({
+        idRaw: cells[0] ?? '',
+        tagsRaw: cells[7] ?? '',
+      });
     }
 
     this.logger.log(