@strapi/data-transfer
Version:
Data transfer capabilities for Strapi
152 lines (148 loc) • 6.07 kB
JavaScript
;
var path = require('path');
var stream = require('stream');
var fs = require('fs-extra');
function getFileStream(filepath, strapi, isLocal = false) {
if (isLocal) {
// Todo: handle errors
return fs.createReadStream(filepath);
}
const readableStream = new stream.PassThrough();
// fetch the image from remote url and stream it
strapi.fetch(filepath).then((res)=>{
if (res.status !== 200) {
readableStream.emit('error', new Error(`Request failed with status code ${res.status}`));
return;
}
if (res.body) {
// pipe the image data
stream.Readable.fromWeb(res.body).pipe(readableStream);
} else {
readableStream.emit('error', new Error('Empty data found for file'));
}
}).catch((error)=>{
readableStream.emit('error', error);
});
return readableStream;
}
function getFileStatsForTransfer(filepath, strapi, isLocal = false) {
if (isLocal) {
return fs.stat(filepath);
}
return new Promise((resolve, reject)=>{
strapi.fetch(filepath).then((res)=>{
if (res.status !== 200) {
reject(new Error(`Request failed with status code ${res.status}`));
return;
}
const contentLength = res.headers.get('content-length');
const stats = {
size: contentLength ? parseInt(contentLength, 10) : 0
};
resolve(stats);
}).catch((error)=>{
reject(error);
});
});
}
async function signUploadFileForTransfer(strapi, file) {
const { provider } = strapi.plugins.upload;
const { provider: providerName } = strapi.config.get('plugin.upload');
const isPrivate = await provider.isPrivate();
if (file?.provider === providerName && isPrivate) {
const signUrl = async (f)=>{
const signedUrl = await provider.getSignedUrl(f);
f.url = signedUrl.url;
};
await signUrl(file);
if (file.formats) {
for (const format of Object.keys(file.formats)){
await signUrl(file.formats[format]);
}
}
}
}
const missingAssetWarningMessage = (file, filepath, format)=>{
const formatPart = format ? ` (format: ${format})` : '';
return `[Data transfer] Media item ${file.id} (hash: ${file.hash}) exists in database but no corresponding file was found to transfer${formatPart}. Path: ${filepath}`;
};
/**
* Generate and consume assets streams in order to stream each file individually
*/ const createAssetsStream = (strapi, options = {})=>{
const warnMissingAsset = (message)=>{
strapi.log.warn(message);
options.onWarning?.(message);
};
const generator = async function*() {
const stream = strapi.db.queryBuilder('plugin::upload.file')// Create a query builder instance (default type is 'select')
// Fetch all columns
.select('*')// Get a readable stream
.stream();
for await (const file of stream){
const isLocalProvider = file.provider === 'local';
if (!isLocalProvider) {
await signUploadFileForTransfer(strapi, file);
}
const filepath = isLocalProvider ? path.join(strapi.dirs.static.public, file.url) : file.url;
let stats;
try {
stats = await getFileStatsForTransfer(filepath, strapi, isLocalProvider);
} catch (err) {
const code = err && typeof err === 'object' && 'code' in err ? err.code : undefined;
if (code === 'ENOENT') {
warnMissingAsset(missingAssetWarningMessage(file, filepath));
continue;
}
throw err;
}
const stream = getFileStream(filepath, strapi, isLocalProvider);
yield {
metadata: file,
filepath,
filename: file.hash + file.ext,
stream,
stats: {
size: stats.size
}
};
if (file.formats) {
for (const format of Object.keys(file.formats)){
const fileFormat = file.formats[format];
const fileFormatFilepath = isLocalProvider ? path.join(strapi.dirs.static.public, fileFormat.url) : fileFormat.url;
let fileFormatStats;
try {
fileFormatStats = await getFileStatsForTransfer(fileFormatFilepath, strapi, isLocalProvider);
} catch (err) {
const code = err && typeof err === 'object' && 'code' in err ? err.code : undefined;
if (code === 'ENOENT') {
warnMissingAsset(missingAssetWarningMessage(file, fileFormatFilepath, format));
continue;
}
throw err;
}
const fileFormatStream = getFileStream(fileFormatFilepath, strapi, isLocalProvider);
const metadata = {
...fileFormat,
type: format,
id: file.id,
mainHash: file.hash
};
yield {
metadata,
filepath: fileFormatFilepath,
filename: fileFormat.hash + fileFormat.ext,
stream: fileFormatStream,
stats: {
size: fileFormatStats.size
}
};
}
}
}
};
return stream.Duplex.from(generator());
};
exports.createAssetsStream = createAssetsStream;
exports.getFileStatsForTransfer = getFileStatsForTransfer;
exports.signUploadFileForTransfer = signUploadFileForTransfer;
//# sourceMappingURL=assets.js.map