螺旋共享 - 存储
存储模块提供了一个统一的后端-不可知界面,用于管理云存储中的对象. Storage 财务报告和审定财务报表 Bucket 配置为可插件 StorageBackend ——这个包船本身没有云SDK;它定义了界面和通用的桶操作(压缩,元数据拷贝/重名代数,深拷贝/去除)上.
默认的 AWS S3( + Cloudflare R2 镜像) 后端活在一个单独的包中, @adobe/helix-shared-storage-s3,大多数消费者会想安装 与这个.
安装
npm install @adobe/helix-shared-storage @adobe/helix-shared-storage-s3
基本用途
现有S3/R2消费者使用 StorageS3,则 Storage 带有默认 S3(+R2)后端的子类预线,导出自 @adobe/helix-shared-storage-s3数字 :
import { StorageS3 as Storage } from '@adobe/helix-shared-storage-s3';
export async function main(req, context) {
const storage = Storage.fromContext(context);
const bucket = storage.contentBus();
const data = await bucket.get('/my/content.json');
return new Response(data);
}
消费者想要不同的后端(或根本不需要云SDK依赖)构建核心的通用 Storage 直接带有明确 backendFactory 函数 (bucketId, opts) => StorageBackend数字 :
import { Storage } from '@adobe/helix-shared-storage';
import { createDefaultBackendFactory } from '@adobe/helix-shared-storage-s3';
const storage = new Storage({
backendFactory: createDefaultBackendFactory(process.env),
log: console,
});
const bucket = storage.bucket('my-bucket-name');
await bucket.put('/path/to/file.txt', 'Hello World', 'text/plain');
const data = await bucket.get('/path/to/file.txt');
storage.close();
与预定义的小桶合作
存储模块为访问常见的螺旋桶提供了方便的方法:
const storage = Storage.fromContext(context);
const contentBucket = storage.contentBus();
const codeBucket = storage.codeBus();
const mediaBucket = storage.mediaBus();
const configBucket = storage.configBus();
const sourceBucket = storage.sourceBus(); // mirroring to a secondary backend disabled by default
Bucket 操作
存储对象
从响应对象存储数据 :
import { Response } from '@adobe/fetch';
const response = new Response('Hello World', {
headers: {
'content-type': 'text/plain',
'cache-control': 'max-age=3600',
},
});
await bucket.store('/path/to/file.txt', response);
过 { compress: false } 作为第三个参数 store() 以跳过 gzip 压缩 :
await bucket.store('/path/to/file.txt', response, { compress: false });
已经作出的反应 content-encoding: gzip 即使压缩被禁用,也不改变存储方式.
用可选压缩直接存储数据 :
// With compression (default)
await bucket.put('/path/to/file.txt', 'Hello World', 'text/plain', {
'custom-meta': 'value',
});
// Without compression
await bucket.put('/path/to/file.txt', 'Hello World', 'text/plain', {}, false);
获取对象
获取对象内容 :
const data = await bucket.get('/path/to/file.txt');
if (data) {
console.log(data.toString());
}
获取元数据对象内容 :
const meta = {};
const data = await bucket.get('/path/to/file.txt', meta);
if (data) {
console.log('Content:', data.toString());
console.log('Metadata:', meta);
}
只获取元数据 :
const metadata = await bucket.metadata('/path/to/file.txt');
console.log(metadata);
获取对象头 :
const headers = await bucket.head('/path/to/file.txt');
if (headers) {
console.log('Content-Type:', headers.contentType);
console.log('Last-Modified:', headers.lastModified);
}
复制对象
在同一桶内复制 :
await bucket.copy('/source/file.txt', '/destination/file.txt');
元数据操作复制 :
await bucket.copy('/source/file.txt', '/destination/file.txt', {
renameMetadata: {
'old-meta-key': 'new-meta-key',
},
addMetadata: {
'additional-meta': 'value',
},
});
列出对象
列出前缀下的所有对象 :
const { objects } = await bucket.list('path/to/folder/');
objects.forEach((obj) => {
console.log('Key:', obj.key);
console.log('Size:', obj.contentLength);
console.log('Type:', obj.contentType);
console.log('Modified:', obj.lastModified);
});
以浅析方式列出对象( 只有直接的孩子) :
const { objects } = await bucket.list('path/to/folder/', {
shallow: true,
maxItems: 100, // limit result count
});
列表文件夹 :
const folders = await bucket.listFolders('path/to/');
folders.forEach((folder) => {
console.log('Folder:', folder);
});
复制目录树
通过可选过滤复制整个目录树 :
// Copy all files
await bucket.copyDeep('/source/folder/', '/destination/folder/');
// Copy with filter
await bucket.copyDeep(
'/source/folder/',
'/destination/folder/',
(obj) => obj.contentType === 'text/plain',
);
// Copy with metadata options
await bucket.copyDeep(
'/source/folder/',
'/destination/folder/',
() => true,
{
addMetadata: {
'copied-at': new Date().toISOString(),
},
},
);
删除对象
删除单个对象 :
await bucket.remove('/path/to/file.txt');
删除多个对象 :
await bucket.remove([
'/path/to/file1.txt',
'/path/to/file2.txt',
'/path/to/file3.txt',
]);
删除整个目录 :
await bucket.rmdir('/path/to/folder/');
更新元数据
更新现有对象的元数据. meta 可能将自定义、用户定义的密钥和普通密钥混合
系统属性字段名( 如 . contentType)——每个后端识别并应用自己的
适当的子集( 默认的 S3 后端映射) contentType 到对象的 Content-Type
系统属性而不是自定义元数据 :
await bucket.putMeta('/path/to/file.txt', {
contentType: 'text/html',
'new-meta-key': 'new-value',
'updated-at': new Date().toISOString(),
});
putMeta() 完全替换元数据集——任何系统属性字段或自定义密钥不
包含的内容没有保留。 要更新元数据而不意外丢失已有字段, 请使用
getMeta() (单位:千美元) putMeta()- 兼容的对应物 metadata()安全往返:
const meta = await bucket.getMeta('/path/to/file.txt');
meta['updated-at'] = new Date().toISOString();
await bucket.putMeta('/path/to/file.txt', meta);
配置选项
Bucket 名称映射
您可以使用 HELIX_BUCKET_NAMES 环境变量或 bucketNames 选项 :
const storage = new Storage({
backendFactory,
bucketNames: JSON.stringify({
config: 'my-custom-config-bucket',
code: 'my-custom-code-bucket',
content: 'my-custom-content-bucket',
media: 'my-custom-media-bucket',
}),
});
或使用助手函数 :
import { parseBucketNames } from '@adobe/helix-shared-storage';
const bucketMap = parseBucketNames(process.env.HELIX_BUCKET_NAMES);
console.log(bucketMap.content); // 'helix-content-bus' (default) or custom name
后端选项( 如禁用 R2 镜像)
bucket() 而命名的大巴访问器则使用一个不透明的选项包逐字转发到配置的 backendFactory ——核心不解读. 默认的 S3/ R2 后端 (@adobe/helix-shared-storage-s3)确认 { disableR2: true }数字 :
// Disable R2 for a specific bucket
const bucket = storage.contentBus({ disableR2: true });
R2 镜像是否配置 完全没有,以及任何HTTP/出时/重试调试,都是后端包件问题——见 @adobe/helix-shared-storage-s3是因为 createDefaultBackendFactory 用于它读取的 S3/R2 特定环境变量(CLOUDFLARE_ACCOUNT_ID, (中文(简体) ), HELIX_STORAGE_DISABLE_R2, (中文(简体) ), HELIX_HTTP_CONNECTION_TIMEOUT(见a/c.5/49/70,附件).
重要行为
自动压缩
所有通过 put() 财务报告和审定财务报表 store() 除非明确禁用,否则会用gzip自动压缩。 取回对象时自动解压 get()。 。 。 .
路径化
对象密钥中的铅鞭被自动去除. 两者 /path/to/file.txt 财务报告和审定财务报表 path/to/file.txt 指向同一个对象.
元数据信头
某些 HTTP 头被存储为后端系统属性而非自定义元数据 : cache-control, (中文(简体) ), content-type,以及 expires编辑 last-modified 以密钥作为自定义元数据存储头 x-source-last-modified。 。 。 .
并行操作
向每个后端 a 发送写入操作( put, store, copy, remove, put Meta) MirroringBackend 以平行(如S3 + R2与默认后端)来组成;读取操作只查询主机.
批次删除
每个后端在去除多个对象(如默认的S3/R2后端块为1000组以遵守AWS S3的限制)时,都会拥有自己的分批限制.