n8n-nodes-binary-to-url 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -66,7 +66,7 @@ export declare class MemoryStorage {
|
|
|
66
66
|
* Format: {timestamp}-{16-char-hex}
|
|
67
67
|
*/
|
|
68
68
|
static generateFileKey(): string;
|
|
69
|
-
static upload(workflowId: string, data: Buffer, contentType: string, ttl?: number): Promise<{
|
|
69
|
+
static upload(workflowId: string, data: Buffer, contentType: string, fileName: string, ttl?: number): Promise<{
|
|
70
70
|
fileKey: string;
|
|
71
71
|
contentType: string;
|
|
72
72
|
}>;
|
|
@@ -74,6 +74,7 @@ export declare class MemoryStorage {
|
|
|
74
74
|
static download(workflowId: string, fileKey: string): Promise<{
|
|
75
75
|
data: Buffer;
|
|
76
76
|
contentType: string;
|
|
77
|
+
fileName: string;
|
|
77
78
|
} | null>;
|
|
78
79
|
static delete(workflowId: string, fileKey: string): Promise<boolean>;
|
|
79
80
|
static cleanupWorkflowExpired(workflowId: string): void;
|
|
@@ -148,7 +148,7 @@ class MemoryStorage {
|
|
|
148
148
|
static generateFileKey() {
|
|
149
149
|
return (0, crypto_1.randomUUID)();
|
|
150
150
|
}
|
|
151
|
-
static async upload(workflowId, data, contentType, ttl) {
|
|
151
|
+
static async upload(workflowId, data, contentType, fileName, ttl) {
|
|
152
152
|
// Wait for any existing upload for this workflow to complete
|
|
153
153
|
const existingLock = this.uploadLocks.get(workflowId);
|
|
154
154
|
if (existingLock) {
|
|
@@ -165,7 +165,7 @@ class MemoryStorage {
|
|
|
165
165
|
// Create new lock for this upload
|
|
166
166
|
const lockPromise = (async () => {
|
|
167
167
|
try {
|
|
168
|
-
return await this.uploadInternal(workflowId, data, contentType, ttl);
|
|
168
|
+
return await this.uploadInternal(workflowId, data, contentType, fileName, ttl);
|
|
169
169
|
}
|
|
170
170
|
finally {
|
|
171
171
|
// Release lock
|
|
@@ -175,7 +175,7 @@ class MemoryStorage {
|
|
|
175
175
|
this.uploadLocks.set(workflowId, lockPromise);
|
|
176
176
|
return lockPromise;
|
|
177
177
|
}
|
|
178
|
-
static async uploadInternal(workflowId, data, contentType, ttl) {
|
|
178
|
+
static async uploadInternal(workflowId, data, contentType, fileName, ttl) {
|
|
179
179
|
const fileKey = this.generateFileKey();
|
|
180
180
|
const now = Date.now();
|
|
181
181
|
const expiresAt = now + (ttl || this.DEFAULT_TTL);
|
|
@@ -184,6 +184,7 @@ class MemoryStorage {
|
|
|
184
184
|
const file = {
|
|
185
185
|
data,
|
|
186
186
|
contentType,
|
|
187
|
+
fileName,
|
|
187
188
|
uploadedAt: now,
|
|
188
189
|
expiresAt,
|
|
189
190
|
};
|
|
@@ -235,6 +236,7 @@ class MemoryStorage {
|
|
|
235
236
|
return {
|
|
236
237
|
data: file.data,
|
|
237
238
|
contentType: file.contentType,
|
|
239
|
+
fileName: file.fileName,
|
|
238
240
|
};
|
|
239
241
|
}
|
|
240
242
|
static async delete(workflowId, fileKey) {
|
|
@@ -90,9 +90,10 @@ class BinaryToUrl {
|
|
|
90
90
|
}
|
|
91
91
|
// Return binary file directly
|
|
92
92
|
const isDownload = constants_js_1.DOWNLOAD_MIME_TYPES.includes(result.contentType);
|
|
93
|
-
const
|
|
93
|
+
const dispositionType = isDownload
|
|
94
94
|
? constants_js_1.HTTP_HEADERS.DISPOSITION_ATTACHMENT
|
|
95
95
|
: constants_js_1.HTTP_HEADERS.DISPOSITION_INLINE;
|
|
96
|
+
const disposition = `${dispositionType}; filename="${result.fileName}"`;
|
|
96
97
|
// Get origin from headers for CORS
|
|
97
98
|
const headers = this.getHeaderData();
|
|
98
99
|
const origin = headers?.origin;
|
|
@@ -213,8 +214,24 @@ async function handleUpload(context, items) {
|
|
|
213
214
|
const binaryData = context.helpers.assertBinaryData(i, binaryPropertyName);
|
|
214
215
|
buffer = await context.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
215
216
|
contentType = binaryData.mimeType || 'application/octet-stream';
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
// Auto-detect file extension:
|
|
218
|
+
// 1. From original filename in binary data
|
|
219
|
+
// 2. From MIME type
|
|
220
|
+
if (binaryData.fileName && typeof binaryData.fileName === 'string') {
|
|
221
|
+
const lastDot = binaryData.fileName.lastIndexOf('.');
|
|
222
|
+
if (lastDot > 0 && lastDot < binaryData.fileName.length - 1) {
|
|
223
|
+
fileExtension = binaryData.fileName.slice(lastDot + 1);
|
|
224
|
+
debugLog(context.logger, `Extracted extension from filename ${binaryData.fileName}: ${fileExtension}`);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
228
|
+
debugLog(context.logger, `No extension in filename, using MIME type: ${fileExtension}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
233
|
+
debugLog(context.logger, `No filename available, using MIME type: ${fileExtension}`);
|
|
234
|
+
}
|
|
218
235
|
debugLog(context.logger, `Got binary data from property: size=${buffer.length}, mimeType=${contentType}, extension=${fileExtension}`);
|
|
219
236
|
}
|
|
220
237
|
catch {
|
|
@@ -226,8 +243,24 @@ async function handleUpload(context, items) {
|
|
|
226
243
|
const stream = await context.helpers.getBinaryStream(fileId);
|
|
227
244
|
buffer = await context.helpers.binaryToBuffer(stream);
|
|
228
245
|
contentType = String(item.json?.mimeType || 'application/octet-stream');
|
|
229
|
-
|
|
230
|
-
|
|
246
|
+
// Auto-detect file extension:
|
|
247
|
+
// 1. From original filename in item data
|
|
248
|
+
// 2. From MIME type
|
|
249
|
+
if (item.json?.fileName && typeof item.json.fileName === 'string') {
|
|
250
|
+
const lastDot = item.json.fileName.lastIndexOf('.');
|
|
251
|
+
if (lastDot > 0 && lastDot < item.json.fileName.length - 1) {
|
|
252
|
+
fileExtension = item.json.fileName.slice(lastDot + 1);
|
|
253
|
+
debugLog(context.logger, `Extracted extension from filename ${item.json.fileName}: ${fileExtension}`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
257
|
+
debugLog(context.logger, `No extension in filename, using MIME type: ${fileExtension}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
262
|
+
debugLog(context.logger, `No filename available, using MIME type: ${fileExtension}`);
|
|
263
|
+
}
|
|
231
264
|
debugLog(context.logger, `File fetched via getBinaryStream: size=${buffer.length}, mimeType=${contentType}, extension=${fileExtension}`);
|
|
232
265
|
}
|
|
233
266
|
catch (streamError) {
|
|
@@ -248,11 +281,12 @@ async function handleUpload(context, items) {
|
|
|
248
281
|
if (fileSize > constants_js_1.CACHE_LIMITS.MAX_FILE_SIZE) {
|
|
249
282
|
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `File size exceeds maximum limit of ${constants_js_1.CACHE_LIMITS.MAX_FILE_SIZE / 1024 / 1024}MB`);
|
|
250
283
|
}
|
|
251
|
-
// Upload to memory storage
|
|
252
|
-
const result = await MemoryStorage_js_1.MemoryStorage.upload(workflowId, buffer, contentType, ttlMs);
|
|
253
|
-
const proxyUrl = `${webhookUrlBase}?fileKey=${result.fileKey}`;
|
|
254
284
|
// Generate random filename with extension
|
|
255
285
|
const randomFileName = fileExtension ? `${(0, crypto_1.randomUUID)()}.${fileExtension}` : (0, crypto_1.randomUUID)();
|
|
286
|
+
debugLog(context.logger, `Generated random filename: ${randomFileName}`);
|
|
287
|
+
// Upload to memory storage
|
|
288
|
+
const result = await MemoryStorage_js_1.MemoryStorage.upload(workflowId, buffer, contentType, randomFileName, ttlMs);
|
|
289
|
+
const proxyUrl = `${webhookUrlBase}?fileKey=${result.fileKey}`;
|
|
256
290
|
debugLog(context.logger, `File uploaded: fileKey=${result.fileKey}, fileName=${randomFileName}`);
|
|
257
291
|
returnData.push({
|
|
258
292
|
json: {
|
package/package.json
CHANGED