|
| 1 | +const fs = require('fs'); |
| 2 | +const path = require('path'); |
1 | 3 | const ExcelJS = require('exceljs'); |
2 | 4 | const excelStyleHelper = require('./excel-style-helper'); |
3 | 5 | const FileUtils = require('./file-utils'); |
@@ -61,6 +63,92 @@ class ExcelGenerator { |
61 | 63 | this.fileUtils = FileUtils; |
62 | 64 | } |
63 | 65 |
|
| 66 | + async exportPerSheetFiles(options) { |
| 67 | + const { sheets, outputPath, format } = options; |
| 68 | + const ext = FileUtils.getExtension(outputPath).toLowerCase(); |
| 69 | + const dir = FileUtils.getDirname(outputPath); |
| 70 | + const base = FileUtils.getBasename(outputPath); |
| 71 | + const extNoDot = ext.startsWith('.') ? ext.slice(1) : ext; |
| 72 | + const targetDir = path.join(dir, `${base}_${extNoDot}`); |
| 73 | + if (!fs.existsSync(targetDir)) { |
| 74 | + fs.mkdirSync(targetDir, { recursive: true }); |
| 75 | + } |
| 76 | + const delimiter = format === 'txt' ? '\t' : ','; |
| 77 | + const withBOM = true; |
| 78 | + const crlf = '\r\n'; |
| 79 | + |
| 80 | + function sanitizeFilename(name) { |
| 81 | + const replaced = String(name).replace(/[\\/:*?"<>|]/g, '_').trim(); |
| 82 | + return replaced.substring(0, 100) || 'sheet'; |
| 83 | + } |
| 84 | + function sanitizeIdentifier(name) { |
| 85 | + let id = String(name).replace(/[^A-Za-z0-9_]/g, '_'); |
| 86 | + if (/^[0-9]/.test(id)) id = 'T_' + id; |
| 87 | + return id || 'T_SHEET'; |
| 88 | + } |
| 89 | + function escapeCsv(val) { |
| 90 | + if (val === null || val === undefined) return ''; |
| 91 | + const s = String(val); |
| 92 | + if (s.includes('"') || s.includes('\n') || s.includes('\r') || s.includes(delimiter)) { |
| 93 | + return '"' + s.replace(/"/g, '""') + '"'; |
| 94 | + } |
| 95 | + return s; |
| 96 | + } |
| 97 | + function toSqlLiteral(val) { |
| 98 | + if (val === null || val === undefined) return 'NULL'; |
| 99 | + if (typeof val === 'number') return String(val); |
| 100 | + if (typeof val === 'boolean') return val ? '1' : '0'; |
| 101 | + if (val instanceof Date) return `'${val.toISOString().slice(0, 19).replace('T', ' ')}'`; |
| 102 | + const s = String(val).replace(/'/g, "''"); |
| 103 | + return `'${s}'`; |
| 104 | + } |
| 105 | + |
| 106 | + for (const sheetDef of sheets) { |
| 107 | + if (!this.isSheetEnabled(sheetDef)) continue; |
| 108 | + const baseName = sheetDef.originalName || sheetDef.name; |
| 109 | + const safeName = sanitizeFilename(baseName); |
| 110 | + const filePath = path.join(targetDir, `${safeName}${ext}`); |
| 111 | + FileUtils.ensureDirExists(filePath); |
| 112 | + const rows = Array.isArray(sheetDef.data) ? sheetDef.data : []; |
| 113 | + |
| 114 | + if (format === 'sql') { |
| 115 | + const colsSet = new Set(); |
| 116 | + rows.forEach(r => Object.keys(r || {}).forEach(k => colsSet.add(k))); |
| 117 | + const columns = Array.from(colsSet); |
| 118 | + const table = sanitizeIdentifier(baseName); |
| 119 | + const lines = []; |
| 120 | + if (columns.length === 0) { |
| 121 | + const single = `-- No data`; |
| 122 | + lines.push(single); |
| 123 | + } else { |
| 124 | + for (const r of rows) { |
| 125 | + const values = columns.map(c => toSqlLiteral(r && r[c] !== undefined ? r[c] : null)).join(', '); |
| 126 | + lines.push(`INSERT INTO ${table} (${columns.map(c => `[${c}]`).join(', ')}) VALUES (${values});`); |
| 127 | + } |
| 128 | + } |
| 129 | + const content = lines.join(crlf) + crlf; |
| 130 | + const data = withBOM ? Buffer.from('\ufeff' + content, 'utf8') : Buffer.from(content, 'utf8'); |
| 131 | + fs.writeFileSync(filePath, data); |
| 132 | + console.log(`[WRITE] ${filePath}`); |
| 133 | + continue; |
| 134 | + } |
| 135 | + |
| 136 | + const colsSet = new Set(); |
| 137 | + rows.forEach(r => Object.keys(r || {}).forEach(k => colsSet.add(k))); |
| 138 | + const columns = Array.from(colsSet); |
| 139 | + const lines = []; |
| 140 | + if (columns.length > 0) lines.push(columns.join(delimiter)); |
| 141 | + for (const r of rows) { |
| 142 | + const vals = columns.map(c => escapeCsv(r && r[c] !== undefined ? r[c] : '')); |
| 143 | + lines.push(vals.join(delimiter)); |
| 144 | + } |
| 145 | + const content = lines.join(crlf) + crlf; |
| 146 | + const data = withBOM ? Buffer.from('\ufeff' + content, 'utf8') : Buffer.from(content, 'utf8'); |
| 147 | + fs.writeFileSync(filePath, data); |
| 148 | + console.log(`[WRITE] ${filePath}`); |
| 149 | + } |
| 150 | + } |
| 151 | + |
64 | 152 | /** |
65 | 153 | * 엑셀 파일 생성 |
66 | 154 | * @param {Object} options - 생성 옵션 |
|
0 commit comments