From a2d04fdad29aeceb820bb65a363eeace6a1e12b4 Mon Sep 17 00:00:00 2001 From: Christian Capeans Date: Mon, 5 Dec 2022 11:44:48 +0100 Subject: [PATCH] Create the assets stream --- .../__tests__/index.test.ts | 19 ++++++++++ .../index.ts} | 38 +++++++++++++++++-- .../local-strapi-destination-provider.ts | 22 ++++++++++- 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 packages/core/data-transfer/lib/providers/local-file-source-provider/__tests__/index.test.ts rename packages/core/data-transfer/lib/providers/{local-file-source-provider.ts => local-file-source-provider/index.ts} (86%) diff --git a/packages/core/data-transfer/lib/providers/local-file-source-provider/__tests__/index.test.ts b/packages/core/data-transfer/lib/providers/local-file-source-provider/__tests__/index.test.ts new file mode 100644 index 0000000000..bf5b2ae677 --- /dev/null +++ b/packages/core/data-transfer/lib/providers/local-file-source-provider/__tests__/index.test.ts @@ -0,0 +1,19 @@ +import type { ILocalFileSourceProviderOptions } from '..'; + +import { Readable } from 'stream'; + +import { createLocalFileSourceProvider } from '..'; + +describe('Stream assets', () => { + test('returns a stream', () => { + const options: ILocalFileSourceProviderOptions = { + backupFilePath: './test-file', + compressed: false, + encrypted: false, + }; + const provider = createLocalFileSourceProvider(options); + const stream = provider.streamAssets(); + + expect(stream instanceof Readable).toBeTruthy(); + }); +}); diff --git a/packages/core/data-transfer/lib/providers/local-file-source-provider.ts b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts similarity index 86% rename from packages/core/data-transfer/lib/providers/local-file-source-provider.ts rename to packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts index 6b727eb9b4..0741bf33d5 100644 --- a/packages/core/data-transfer/lib/providers/local-file-source-provider.ts +++ b/packages/core/data-transfer/lib/providers/local-file-source-provider/index.ts @@ -1,17 +1,18 @@ import type { Readable } from 'stream'; -import type { IMetadata, ISourceProvider, ProviderType } from '../../types'; +import type { IMetadata, ISourceProvider, ProviderType } from '../../../types'; import fs from 'fs'; import zip from 'zlib'; import tar from 'tar'; +import path from 'path'; import { keyBy } from 'lodash/fp'; import { chain } from 'stream-chain'; -import { pipeline, PassThrough } from 'stream'; +import { pipeline, PassThrough, Readable as ReadStream } from 'stream'; import { parser } from 'stream-json/jsonl/Parser'; -import { createDecryptionCipher } from '../encryption'; -import { collect } from '../utils'; +import { createDecryptionCipher } from '../../encryption'; +import { collect } from '../../utils'; type StreamItemArray = Parameters[0]; @@ -109,6 +110,35 @@ class LocalFileSourceProvider implements ISourceProvider { return this.#streamJsonlDirectory('configuration'); } + streamAssets(): NodeJS.ReadableStream { + const inStream = this.#getBackupStream(); + const outStream = new PassThrough({ objectMode: true }); + + pipeline( + [ + inStream, + new tar.Parse({ + filter(path, entry) { + if (entry.type !== 'File') { + return false; + } + + const parts = path.split('/'); + return parts[0] === 'assets' && parts[1] == 'uploads'; + }, + onentry(entry) { + const { path: filePath, size } = entry; + const file = path.basename(filePath); + outStream.write({ file, path: filePath, stats: { size }, stream: entry }); + }, + }), + ], + () => outStream.end() + ); + + return outStream; + } + #getBackupStream(decompress: boolean = true) { const path = this.options.backupFilePath; const readStream = fs.createReadStream(path); diff --git a/packages/core/data-transfer/lib/providers/local-strapi-destination-provider.ts b/packages/core/data-transfer/lib/providers/local-strapi-destination-provider.ts index 24105235f6..934b52a96e 100644 --- a/packages/core/data-transfer/lib/providers/local-strapi-destination-provider.ts +++ b/packages/core/data-transfer/lib/providers/local-strapi-destination-provider.ts @@ -2,7 +2,9 @@ import type { IDestinationProvider, IMetadata, ProviderType } from '../../types'; import chalk from 'chalk'; -import { Duplex } from 'stream'; +import { Duplex, Writable } from 'stream'; +import path from 'path'; +import * as fse from 'fs-extra'; import { mapSchemasValues } from '../utils'; @@ -57,6 +59,24 @@ class LocalStrapiDestinationProvider implements IDestinationProvider { return mapSchemasValues(schemas); } + getAssetsStream(): NodeJS.WritableStream { + if (!this.strapi) { + throw new Error('Not able to stream Assets. Strapi instance not found'); + } + const assetsDirectory = path.join(this.strapi.dirs.static.public, 'uploads'); + return new Writable({ + objectMode: true, + write(chunk, _encoding, callback) { + const entryPath = path.join(assetsDirectory, chunk.file); + const writableStream = fse.createWriteStream(entryPath); + chunk.stream + .pipe(writableStream) + .on('close', () => callback()) + .on('error', callback); + }, + }); + } + getEntitiesStream(): Duplex { const self = this;