导出资源
了解如何使用可自定义的Node.js脚本将资产导出到本地计算机。 此导出脚本提供了一个示例,说明如何使用AEM Assets HTTP API以编程方式从AEM下载资源,尤其侧重于原始演绎版以确保最高质量。 它设计为可在本地驱动器上复制AEM Assets的文件夹结构,从而轻松备份或迁移资产。
该脚本仅下载资源的原始演绎版,不带关联的元数据,除非该元数据已作为XMP嵌入到资源中。 这意味着下载中不包含存储在AEM中但未集成到资源文件中的任何描述性信息、分类或标记。 也可以通过修改脚本以包含其他演绎版来下载它们。 确保您有足够的空间来存储导出的资源。
脚本通常针对AEM Author运行,但也可以针对AEM Publish运行,前提是可以通过Dispatcher访问AEM Assets HTTP API端点和资源演绎版。
在运行脚本之前,您必须使用AEM实例URL、用户凭据(访问令牌)以及要导出的文件夹的路径对其进行配置。
导出脚本
编写为JavaScript模块的脚本是Node.js项目的一部分,因为它依赖于node-fetch
。 您可以将该项目下载为zip文件,或者将下面的脚本复制到类型为module
的空Node.js项目中,然后运行npm install node-fetch
以安装依赖项。
此脚本将浏览AEM Assets文件夹树,并将资源和文件夹下载到计算机上的本地文件夹。 它使用AEM Assets HTTP API获取文件夹和资源数据,并下载资源的原始演绎版。
// export-assets.js
import fetch from 'node-fetch';
import { promises as fs } from 'fs';
import path from 'path';
// Do not process the contents of these well-known AEM system folders
const SKIP_FOLDERS = ['/content/dam/appdata', '/content/dam/projects', '/content/dam/_CSS', '/content/dam/_DMSAMPLE' ];
/**
* Determine if the folder should be processed based on the entity and AEM path.
*
* @param {Object} entity the AEM entity that should represent a folder returned from AEM Assets HTTP API
* @param {String} aemPath the path in AEM of this source
* @returns true if the entity should be processed, false otherwise
*/
function isValidFolder(entity, aemPath) {
if (aemPath === '/content/dam') {
// Always allow processing /content/dam
return true;
} else if (!entity.class.includes('assets/folder')) {
return false;
} if (SKIP_FOLDERS.find((path) => path === aemPath)) {
return false;
} else if (entity.properties.hidden) {
return false;
}
return true;
}
/**
* Determine if the entity is downloadable.
* @param {Object} entity the AEM entity that should represent an asset returned from AEM Assets HTTP API
* @returns true if the entity is downloadable, false otherwise
*/
function isDownloadable(entity) {
if (entity.class.includes('assets/folder')) {
return false;
} else if (entity.properties.contentFragment) {
return false;
}
return true;
}
/**
* Helper function to get the link from the entity based on the relationship name.
* @param {Object} entity the entity from the AEM Assets HTTP API
* @param {String} rel the relationship name
* @returns
*/
function getLink(entity, rel) {
return entity.links.find(link => link.rel.includes(rel));
}
/**
* Helper function to fetch JSON data from the AEM Assets HTTP API.
* @param {String} url the AEM Assets HTTP API URL to fetch data from
* @returns the JSON response of the AEM Assets HTTP API
*/
async function fetchJSON(url) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${AEM_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
return response.json();
}
/**
* Helper function to download a file from AEM Assets.
* @param {String} url the URL of the asset rendition to download
* @param {String} outputPath the local path to save the downloaded file
*/
async function downloadFile(url, outputPath) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${AEM_ACCESS_TOKEN}`,
}
});
if (!response.ok) {
throw new Error(`Failed to download file: ${response.statusText}`);
}
const arrayBuffer = await response.arrayBuffer();
await fs.writeFile(outputPath, Buffer.from(arrayBuffer));
console.log(`Downloaded asset: ${outputPath}`);
}
/**
* Main entry
* @param {Object} options the options for downloading assets
* @param {String} options.folderUrl the URL of the AEM folder to download
* @param {String} options.localPath the local path to save the downloaded assets
* @param {String} options.aemPath the AEM path of the folder to download
*/
async function downloadAssets({apiUrl, localPath = LOCAL_DOWNLOAD_FOLDER, aemPath = '/content/dam'}) {
if (!apiUrl) {
// Handle the initial call to the script, which should just provide the AEM path
// Construct the proper AEM Assets HTTP API URL as it uses a truncated AEM path
const prefix = "/content/dam/";
let apiPath = aemPath.startsWith(prefix) ? aemPath.substring(prefix.length) : aemPath;
if (!apiPath.startsWith('/')) {
apiPath = '/' + apiPath;
}
apiUrl = `${AEM_HOST}/api/assets.json${apiPath}`
}
const data = await fetchJSON(apiUrl);
const entities = data.entities || [];
// Process folders first
for (const folder of entities.filter(entity => entity.class.includes('assets/folder'))) {
const newLocalPath = path.join(localPath, folder.properties.name);
const newAemPath = path.join(aemPath, folder.properties.name);
if (!isValidFolder(folder, newAemPath)) {
continue;
}
await fs.mkdir(newLocalPath, { recursive: true });
await downloadAssets({
apiUrl: getLink(folder, 'self')?.href,
localPath: newLocalPath,
aemPath: newAemPath
});
}
let downloads = [];
// Process assets
for (const asset of entities.filter(entity => entity.class.includes('assets/asset'))) {
const assetLocalPath = path.join(localPath, asset.properties.name);
if (isDownloadable(asset)) {
downloads.push(downloadFile(getLink(asset, 'content')?.href, assetLocalPath));
}
// Process in batches of MAX_CONCURRENT_DOWNLOADS
if (downloads.length >= MAX_CONCURRENT_DOWNLOADS) {
await Promise.all(downloads);
downloads = [];
}
}
// Wait for the remaining downloads to finish
await Promise.all(downloads);
downloads = [];
// Handle pagination
const nextUrl = getLink(data, 'next');
if (nextUrl) {
await downloadAssets({
apiUrl: nextUrl?.href,
localPath,
aemPath
});
}
}
/***** SCRIPT CONFIGURATION *****/
// AEM host is the URL of the AEM environment to download the assets from
const AEM_HOST = 'https://author-p123-e456.adobeaemcloud.com';
// AEM access token used to access the AEM host.
// This access token must have read access to the folders and assets to download.
const AEM_ACCESS_TOKEN = "eyJhbGciOiJS...zCprYZD0rSjg6g";
// The root folder in AEM to download assets from.
const AEM_ASSETS_FOLDER = '/content/dam/wknd-shared';
// The local folder to save the downloaded assets.
const LOCAL_DOWNLOAD_FOLDER = './exported-assets';
// The number of maximum concurrent downloads to avoid overwhelming the client or server. 10 is typically a good value.
const MAX_CONCURRENT_DOWNLOADS = 10;
/***** SCRIPT ENTRY POINT *****/
console.time('Download AEM assets');
await downloadAssets({
aemPath: AEM_ASSETS_FOLDER,
localPath: LOCAL_DOWNLOAD_FOLDER
}).catch(console.error);
console.timeEnd('Download AEM assets');
配置导出
下载脚本后,更新脚本底部的配置变量。
可以使用对AEM as a Cloud Service🔗进行基于令牌的身份验证教程中的步骤来获取AEM_ACCESS_TOKEN
。 通常,只要导出过程不超过24小时,并且生成令牌的用户拥有对要导出的资产的读取权限,24小时开发人员令牌便足以满足需求。
...
/***** SCRIPT CONFIGURATION *****/
// AEM host is the URL of the AEM environment to download the assets from
const AEM_HOST = 'https://author-p123-e456.adobeaemcloud.com';
// AEM access token used to access the AEM host.
// This access token must have read access to the folders and assets to download.
const AEM_ACCESS_TOKEN = "eyJhbGciOiJS...zCprYZD0rSjg6g";
// The root folder in AEM to download assets from.
const AEM_ASSETS_FOLDER = '/content/dam/wknd-shared';
// The local folder to save the downloaded assets.
const LOCAL_DOWNLOAD_FOLDER = './export-assets';
// The number of maximum concurrent downloads to avoid overwhelming the client or server. 10 is typically a good value.
const MAX_CONCURRENT_DOWNLOADS = 10;
导出资源
使用Node.js运行脚本,将资产导出到本地计算机。
根据资源的数量及其大小,脚本可能需要一些时间才能完成。 脚本执行时,它将进度记录到控制台。
$ node export-assets.js
导出输出
导出脚本将进度记录到控制台,指示正在下载的资源。 脚本完成时,资产将保存到配置中指定的本地文件夹,并且日志以下载资产所花费的总时间结束。
...
Downloaded asset: exported-assets/wknd-shared/en/magazine/skitouring/skitouring3sjoeberg.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/skitouring/skitouring5sjoeberg.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/skitouring/skitouring6sjoeberg.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/wa_camping_adobe.pdf
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/adobestock-156407519.jpeg
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/adobe-waadobe-wa-mg-3094.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/adobe-waadobe-wa-mg-3851.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/adobe-waadobe-wa-b6a7083.jpg
Downloaded asset: exported-assets/wknd-shared/en/magazine/western-australia/adobe-waadobe-wa-b6a6978.jpg
Download AEM assets: 24.770s
在配置LOCAL_DOWNLOAD_FOLDER
中指定的本地文件夹中可以找到导出的资源。 文件夹结构反映了AEM Assets文件夹结构,并将资源下载到相应的子文件夹。 这些文件可以上载到支持的云存储提供商,以便将批量导入到其他AEM实例,或用于备份。