@backflaze-labs/b2-sdk (英语)
用于 Backblaze B2 Cloud 存储的后火焰维护型号Script 和 JavaScript SDK,目前正在孵化 后燃实验室。 。 。 .
源头层次的Isomorphic. 一棵源树在Node.js 22.3+, Bun, Deno,和浏览器中运行未被修改,并围绕Cloudflare Workers和Vercel Edge使用的同一种Web API来设计. 国内进口用途 .ts 扩展名, 因此 Deno 读取 src/ 直接没有建设步骤。 见 源异形论。 。 。 .
先参. 建于网络流上, AbortSignal,以及 crypto.subtle。没有召回,没有遗留的API.
满打入. 标记的ID, 歧视性的加密设置和错误联盟, 严格的类型脚本 exactOptionalPropertyTypes。 。 。 .
零运行时间依赖关系. 核心软件包没有 dependencies 内 package.json。 。 。 .
完整 API 参考 : 后火焰-labs.github.io/b2-sdk-typescript (从源头产生 在每次推向 main) (中文(简体) ).
安装
npm install @backblaze-labs/b2-sdk
# or
pnpm add @backblaze-labs/b2-sdk
# or
yarn add @backblaze-labs/b2-sdk
快开始
import { B2Client, BufferSource } from '@backblaze-labs/b2-sdk'
const client = new B2Client({
applicationKeyId: process.env.B2_APPLICATION_KEY_ID,
applicationKey: process.env.B2_APPLICATION_KEY,
})
await client.authorize()
const bucket = await client.getBucket('my-app-data')
if (!bucket) throw new Error('bucket not found')
const data = new TextEncoder().encode('Hello, B2!')
const file = await bucket.upload({
fileName: 'hello.txt',
source: new BufferSource(data),
contentType: 'text/plain',
})
console.log(`Uploaded: ${file.fileName} (${file.contentLength} bytes)`)
特征
弹夹
import { BucketType } from '@backblaze-labs/b2-sdk'
// List all buckets
const buckets = await client.listBuckets()
// Get a bucket by name
const bucket = await client.getBucket('my-bucket')
// Update bucket settings
await bucket.update({
bucketType: BucketType.AllPublic,
lifecycleRules: [{ fileNamePrefix: 'logs/', daysFromUploadingToHiding: 30 }],
})
// Delete a bucket
await bucket.delete()
这个
BucketType, (中文(简体) ),RetentionMode, (中文(简体) ),LegalHoldValue, (中文(简体) ),Capability, (中文(简体) ),EventType,以及EncryptionModeas const从主条目导出的对象为您提供了原始字符串字元的无字型替代物——选择您喜欢的样式;两种字型都接受.
合伙人和计算机备份 API
合作伙伴 API 和计算机备份 API 帮助器从专用子路径导出 :
import { PartnerClient } from '@backblaze-labs/b2-sdk/partner'
import { BackupClient } from '@backblaze-labs/b2-sdk/backup'
两个客户通过合作伙伴授权流授权使用主应用程序密钥. 创建账户的合作伙伴业务,包括集团成员的创建和审判预订,需要管理员账户上的有效短信电话号码. 见 实例/README.md 用于当前先决条件和可操作示例.
上传
小型文件(在所推荐的部分大小下,一般为100 MB)在一次请求中被上传. 较大文件自动使用多段上传并平行部分上传.
import { BufferSource, BlobSource, FileSource, toContentSource } from '@backblaze-labs/b2-sdk'
// From a Uint8Array
await bucket.upload({
fileName: 'data.bin',
source: new BufferSource(myUint8Array),
})
// From a Blob or File (browser)
await bucket.upload({
fileName: 'photo.jpg',
source: new BlobSource(fileInput.files[0]),
contentType: 'image/jpeg',
})
// From a local file path (Node)
await bucket.upload({
fileName: 'backups/db.dump',
source: await FileSource.fromPath('/var/backups/db.dump'),
})
// From a forward-only stream or async iterable (size required)
await bucket.upload({
fileName: 'exports/report.ndjson',
source: toContentSource(nodeReadable, knownByteLength),
})
// Large file with progress tracking (Node)
await bucket.upload({
fileName: 'backup.tar.gz',
source: await FileSource.fromPath('/path/to/backup.tar.gz'),
concurrency: 8,
partSize: 64 * 1024 * 1024,
onProgress: (event) => {
console.log(`${event.bytesTransferred}/${event.totalBytes} bytes`)
},
signal: AbortSignal.timeout(300_000),
})
FileSource 拒绝非正则叶路径,后期叶路径互换,并拒绝在POSIX平台上恢复 mtime的同大小重写. 除非您的应用程序先验证包含的根,否则会遵循父目录的同义链接 。 在视窗上, FileSource 跳过不可靠的 dev/inode和时间比较,并验证大小和 mtime.
瞬间上传失败用新鲜的B2上传URL进行再试,与B2所记录的流量相匹配. 如果第一个上传的 POST 成功,但其响应被丢失,重试可以创建重复的文件版本.
使用 onUploadRetry 以登录或计数尝试,在调和上传时比较返回的文件ID和SHA-1值,并为必须自动清理重复版本的桶配置生命周期或版本保留规则。 重试诊断仅仅是事件:SDK不记录,计数,或坚持,除非这个听众记录. 有效载荷重置 POST 和新鲜的 URL 接取器花费了一次上传重试预算, 并且以 retry.maxRetries + 1 尝试每个文件或部分; 使用多段传输同位素的集合重试缩放 。 在同一个上传URL上上上传429节奏后退,而不是取取一个新的.
上传不重试丢失的成功响应机构, 或默认上传 POST 网络错误, 因为重发 b2_upload_file 可以创建重复版本,特别是在版本或对象锁定桶中. 如果调用者选择这种模糊性 retryResponseBodyFailures: true,可重试上传 POST 网络错误和无法读取的响应机构可以使用新的 URL 重新投放有效载荷,以 retry.maxRetries; 任何上传 fileRetention 或者说 legalHold 适用于每个重复版本,并可防止删除,直至保留政策期满或搁置被清除。 多段调用者在重播时也可以选择使用 partNumber 可在 finishLargeFile,包括SSE-B2加密部件.
大文件部分回放每部分协调,而不是由共享断路器协调. 在B2舱或主-API事件中,同时部分可以独立地与快克退后,取取新鲜部分URL,并平行地进行再试; 以多块通量的集合新鲜-URL流量尺度和 retry.maxRetries多部分工人在致命部分故障后立即中止兄弟间工作,最佳清理工作被限定,因此中止工作不会无限期地被吊死。 调色板 concurrency, (中文(简体) ), retry,以及 onUploadRetry 给那些需要停用计数器或更严格载货板的操作员。 每一次HTTP尝试默认有15分钟的截止时间;对于流取的下载机构,每块后超时被闲置/无进展并重排. 设定 retry.requestTimeoutMs 对于非常慢的上传链接或到 0 只能依靠你自己 AbortSignal。 。 。 .
FileSource 是 Node.js 唯一的内容适配器。 从这里进口是安全的
在浏览器中构建主软件包, 但是 FileSource.fromPath() 及其阅读方法
运行时需要本地文件系统 API 。 源文件记录已验证的文件
身份和失败 FileSource file changed after validation 如果路径是
替换、切换、修改内容或有更改时间的元数据更新
当一个多段上传正在读取它时。 在没有平台上 O_NOFOLLOW, (中文(简体) ),
叶-同义互换被打开后的身份检查拒绝,而不是被
打开的旗帜。 在活动作者停止更改文件后重试 .
恢复失败的多段上传
复有两道. resumeFileId 瞄准一个已知的未完成的大文件ID, 当多段上传开始时由 B2 返回. resume: true 无 resumeFileId 运行绑定的同名发现,并可能继续上传到最新兼容的未上传;不兼容的候选者被跳过并开始新的上传。 自动地将每一个计划中的部分重新加载入所选的未完成文件,而不是只靠SHA-1来信任先前存在的服务器部分. 有明确的 resumeFileId,每个本地部分再次被散列,匹配的服务器部分仅在本地重制的SHA-1等于B2的部分SHA-1时被跳出.
重新发现是故意保守的。 SDK只重用最新的未完成的大文件,其文件名称,内容类型,呼叫者提供的文件信息,加密,对象锁定保留,合法持有,并上传部分长度与当前调用相符. 如果这些检查失败, 将开始一个新的大文件 。 调用者提供的文件信息, 如 large_file_sha1 或者说 src_last_modified_millis 是这个身份的一部分,所以保持它的稳定 通过回溯。 发现不会将SDK元数据添加到未完成或已完成的文件信息中.
自动恢复需要稳定的内容类型. 当上传使用 SDK 默认值时 b2/x-auto,同名发现开始一个新的大文件,除非 B2 列出相同未完成的文件 b2/x-auto 数值。 提供明确的 contentType 自动恢复或使用 resumeFileId 当调用者故意信任一个特定的未完成文件所列出的内容类型时. 明确的内容类型仍然需要精确匹配.
自动恢复发现不使用B2存储的部分SHA-1来跳过本地字节,因为B2未完成的大文件记录无法识别启动它们的编目. 它可能仍然会完成一个兼容的同名的未完成上传,在用本地读取的字节来覆盖计划部件后,所以当共享的桶有并发的写作时,使用精确的文件名和稳定的呼叫者拥有的文件信息. 使用 resumeFileId 只有在呼叫者故意信任特定未完成的文件ID时才会使用.
发现扫描是为了避免在共享桶中进行无约束的前缀活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活 默认SDK检查最多可达10个未完成文件页,最多可有25个元数据相容的候选,每个候选者最多可有10个部分页,因此最坏的恢复尝试可以在数据传输开始前添加许多相继的B2列表呼叫. 调色板 resumeMaxListPages, (中文(简体) ), resumeMaxPartCandidates,以及 resumeMaxPartPages 用于许多同时上传同名前缀的桶。 SDK 不安装默认恢复发现超时; 设置 resumeDiscoveryTimeoutMs 或传递中止信号, 如 AbortSignal.timeout(...) 当发现需要第一字节时间预算时。 如果扫描被截断, SDK 报告 search-truncated;当一个相容候选人已被扫描后,该候选人仍然可能被再利用.
被拒绝的候选人和重新使用现有未完成文件的失败尝试仍未完成。 B2存储了未完成的大文件的上传部分,直到完成,取消,或被生命周期规则去除,因此更严格的匹配或重复前缀-花分流可以留下可计费的可完成上传. 严格恢复匹配的应用程序应配置生命周期清理或定期使用 listUnfinishedLargeFiles 财务报告和审定财务报表 cancelLargeFile 删除已过时的上传.
当呼叫者忽略 serverSideEncryption,高级桶和对象上传对照桶当前的默认加密新取自B2,包括默认的SSE-B2或没有加密来验证候选人. 明确 serverSideEncryption: { mode: 'none' } 仍然需要一位未加密的候选人。 SSE- C 上传功能不会自动恢复或随同恢复 resumeFileId 因为B2不会为未完成的文件暴露非秘密客户密钥身份. 与 resume: true,一个SSE-C重试开始一个新的大文件. 与 resumeFileId,SSE-C 失败 ResumeFileIdMismatchError 而不是在未经验证的密钥下冒险完成.
对象锁定状态在再利用前也必须被验证. 当呼叫者忽略 fileRetention,高级桶和对象上传使用B2新取出可读桶默认保留;没有精确要求的默认保留窗口的候选人被跳出. 无法读取的桶默认保留、候选人保留或候选人合法持有的字段未能关闭。 明确保留值或合法持有值仍然需要可读匹配的候选元数据.
过 onResumeCandidateRejected 以收集未完成的同名大文件被跳过时的诊断事件,包括诸如: file-info-mismatch, (中文(简体) ), part-length-mismatch, (中文(简体) ), search-truncated,或 sse-c-unsupported. 恢复诊断是只针对事件:没有这个收听器,保守的恢复回落到没有SDK记录或计数器的新鲜上传. 具体候选人的活动包括: requestedFileName 财务报告和审定财务报表 candidateFileName 因此日志可以区分预定的上传名称和被拒绝的未完成文件. 过 onResumePartReused 以观察被明确接受的每个先前存在的服务器部分 resumeFileId 通过SHA-1平等门. 如果提供 resumeFileId 无法通过B2未完成的大文件列表进行验证, ResumeFileIdMismatchError 被丢弃,而不是默默地启动不同的大文件.
// Restart the upload that crashed at part 47 of 100
await bucket.upload({
fileName: 'backup.tar.gz',
source: new BlobSource(largeBlob),
partSize: 64 * 1024 * 1024,
resumeFileId: knownLargeFileId,
})
通过WritableStream上传
管道任意 ReadableStream<Uint8Array> 直接进入B2 SDK缓冲器最多 partSize 每部分的字节并平行地通过多段协议上传. 后压通过内部排队获得荣誉.
const { writable, done } = bucket.file('logs.ndjson').createWriteStream({
partSize: 16 * 1024 * 1024,
concurrency: 4,
})
// Pipe from any ReadableStream source: fetch().body, fs.createReadStream(...) (with toWeb), etc.
await response.body.pipeTo(writable)
const fileVersion = await done
console.log(`Streamed upload finished: ${fileVersion.fileName} (${fileVersion.contentLength} bytes)`)
流上传不支持恢复,因为总大小和每部分SHA-1不事先知道. 使用缓冲
upload路径resumeFileId当这很重要.
下载
// Download by file name
const result = await bucket.download('hello.txt')
const text = await new Response(result.body).text()
// Download by file ID with range
const partial = await bucket.download('large-file.bin', {
range: 'bytes=0-1023',
})
// Parallel ranged download (for large files).
// Each range uses the client's configured RetryTransport budget, so transient
// 503s are retried without adding a second default retry layer per chunk.
const obj = bucket.file('big-dataset.parquet')
const stream = obj.createReadStream(fileId, totalSize, {
concurrency: 4,
rangeSize: 10 * 1024 * 1024,
})
当 B2 返回真实时, 自动校验全体下载 X-Bz-Content-Sha1 消化系统 如果下载的字节不匹配, 车体流错误与 ChecksumMismatchError; 管道到磁盘时丢弃任何部分写入的输出。 HEAD 请求, 范围获取, 以及 SHA-1 不可用的文件, 无法校验, 因为不存在匹配的全身文摘 .
同步 SHA-1 比较
compareMode: 'sha1' hashe匹配大小的本地文件,并与可核查的B2 SHA-1元数据进行比较. B2 多部分对象没有权威 contentSha1; 仅当 fileInfo.large_file_sha1 或提供另一个不可信提示,一个真正的同步下载,在跳过传输前选择了 B2 版本并拖动全部对象。 SDK并不坚持这个验证结果,因此一个不变的100GB多段对象可以花费100GB的B2下载读取每一次SHA-1同步运行. compare.bytesVerified 报告运行时的 B2 验证字节。 使用 sha1VerificationMaxBytes 将对象跳过每个文件的校验预算, 提高 sha1VerificationTimeoutMillis 对于慢链接上的大型对象,或使用 size编号 :modtime 模式.
B2- to- local同步流在重新命名到原位之前, 在目标根下通过一个私有的运行目录下载 。 扫描仪忽略了管理目录和被扭曲的SDK拥有的中继条目在后期的下载设置上被收录. 新文件默认为0600发布,替换后保留上一个文件模式并使用下载站 downloadIdleTimeoutMillis (默认为60秒)而不是 sha1ReadTimeoutMillis。 。 。 . B2Folder 前缀为生 B2 键前缀:使用 photos/ 用于剪切限制的文件夹,或 photos 用这些字节开头的每个键匹配 .
文件操作
// List files (single page)
const listing = await bucket.listFileNames({ prefix: 'photos/', pageSize: 100 })
// Iterate all files (async generator, handles pagination)
for await (const file of bucket.paginateFileNames({ prefix: 'logs/' })) {
console.log(file.fileName, file.contentLength)
}
// Look up the latest visible version by name (returns null if missing or hidden)
const info = await bucket.getFileInfoByName('hello.txt')
// Fetch metadata without transferring the body (HTTP HEAD). Returns a
// body-less result so callers never have to drain a (logically empty)
// HEAD response stream themselves.
const { headers } = await bucket.head('hello.txt')
console.log(headers.contentLength, headers.contentSha1)
// Hide a file (soft delete)
await bucket.hideFile('old-config.json')
// Restore visibility by removing the latest hide marker
await bucket.unhideFile('old-config.json')
// Delete a specific file version
await bucket.deleteFileVersion('file.txt', fileId)
// Server-side copy (single call, suitable for any size B2 supports)
await bucket.copyFile({
sourceFileId: originalFileId,
fileName: 'copy-of-file.txt',
})
// Server-side multipart copy for large files. Splits the source into parts
// copied in parallel via b2_copy_part. Falls back to copyFile below partSize.
await bucket.copyLargeFile({
sourceFileId: originalFileId,
fileName: 'big-replica.bin',
partSize: 64 * 1024 * 1024,
concurrency: 4,
})
删除批量
两个原始人 Bucket 用于规模清理:
// Delete a known set of file versions with bounded concurrency
const result = await bucket.deleteMany(
[
{ fileName: 'a.txt', fileId: id1 },
{ fileName: 'b.txt', fileId: id2 },
],
{ concurrency: 10 },
)
console.log(`deleted=${result.deleted} errors=${result.errors.length}`)
// Stream-delete every version matching a prefix (or the whole bucket if omitted).
// Yields a DeleteAllEvent per version; never materialises the full list in memory.
for await (const event of bucket.deleteAll({ prefix: 'tmp/', dryRun: false })) {
if (event.type === 'delete') console.log('deleted', event.fileName)
else if (event.type === 'error') console.warn('failed', event.fileName, event.message)
}
应用程序密钥
import { Capability } from '@backblaze-labs/b2-sdk'
const key = await client.createKey({
capabilities: [Capability.ReadFiles, Capability.WriteFiles],
keyName: 'my-app-key',
bucketIds: [bucket.id],
namePrefix: 'uploads/',
validDurationInSeconds: 86400,
})
const keys = await client.listKeys()
await client.deleteKey(key.applicationKeyId)
能力检查
输入出错而不能等待服务器 401/403 失败 :
import { Capability } from '@backblaze-labs/b2-sdk'
import { B2InsufficientCapabilityError } from '@backblaze-labs/b2-sdk/errors'
const required = [Capability.ReadFiles, Capability.WriteFiles]
const { ok, missing } = client.hasCapabilities(required)
if (!ok) {
throw new B2InsufficientCapabilityError(required, [...missing], missing)
}
服务器端加密
import { SSE_B2, sseCustomer } from '@backblaze-labs/b2-sdk'
import { EncryptionKey } from '@backblaze-labs/b2-sdk/streams'
// SSE-B2 (Backblaze-managed keys)
await bucket.upload({
fileName: 'encrypted.dat',
source: new BufferSource(data),
serverSideEncryption: SSE_B2,
})
// SSE-C (customer-provided keys) - precomputed digests
await bucket.upload({
fileName: 'secret.dat',
source: new BufferSource(data),
serverSideEncryption: sseCustomer(base64Key, base64KeyMd5),
})
// SSE-C from raw bytes (Node). EncryptionKey computes the MD5 internally and
// redacts itself in JSON.stringify, toString, and Node's util.inspect so the
// key never lands in logs.
const key = await EncryptionKey.fromBytes(randomBytes(32))
await bucket.upload({
fileName: 'secret.dat',
source: new BufferSource(data),
serverSideEncryption: key,
})
console.log(key) // [EncryptionKey SSE-C [redacted SSE-C key]]
JSON.stringify(key) // customer key and MD5 fields show "[redacted SSE-C key]"
对象锁定和合法持有
import { LegalHoldValue, RetentionMode } from '@backblaze-labs/b2-sdk'
await bucket.updateFileRetention('important.pdf', fileId, {
mode: RetentionMode.Governance,
retainUntilTimestamp: Date.now() + 365 * 24 * 60 * 60 * 1000,
})
// Shorten a governance-mode retention. Requires the bypassGovernance capability.
await bucket.updateFileRetention(
'important.pdf',
fileId,
{ mode: RetentionMode.Governance, retainUntilTimestamp: Date.now() + 24 * 60 * 60 * 1000 },
{ bypassGovernance: true },
)
await bucket.updateFileLegalHold('evidence.pdf', fileId, LegalHoldValue.On)
事件通知
import { EventType } from '@backblaze-labs/b2-sdk'
await bucket.setNotificationRules([
{
name: 'upload-notify',
eventTypes: [EventType.ObjectCreatedAll],
isEnabled: true,
targetConfiguration: {
targetType: 'webhook',
url: 'https://my-app.com/webhooks/b2',
hmacSha256SigningSecret: process.env.B2_WEBHOOK_SECRET,
customHeaders: [{ name: 'X-B2-Source', value: 'uploads' }],
},
},
])
在接收方核查 X-Bz-Event-Notification-Signature 在信任有效载荷之前是头条。 这个 @backblaze-labs/b2-sdk/notifications 子路径船 HMAC-SHA256 帮助者,所以你不必经常执行签名检查自己:
import {
B2_WEBHOOK_SIGNATURE_HEADER,
requireValidWebhook,
} from '@backblaze-labs/b2-sdk/notifications'
// Inside your HTTP handler. `body` must be the raw request bytes — any
// JSON re-serialisation will invalidate the HMAC.
const body = new Uint8Array(await request.arrayBuffer())
const payload = await requireValidWebhook({
body,
signature: request.headers.get(B2_WEBHOOK_SIGNATURE_HEADER),
secret: process.env.B2_WEBHOOK_SECRET,
})
for (const event of payload.events) {
console.log(event.eventType, event.objectName)
}
requireValidWebhook 丢入缺失/无效签名,并在成功时返回解析后的有效载荷。 如果宁可在布尔上分行(例如不投掷记录失败原因),则使用下层 verifyWebhookSignature 返回时 { valid, reason, payload }。 。 。 .
下载授权
// Generate a short-lived download authorization for sharing
const auth = await bucket.getDownloadAuthorization('photos/', 3600)
持续授权(节点)
FileAccountInfo 持续对磁盘上的 JSON 文件的授权响应,这样进程就可以重新开始,而无需重新授权。 它执行 AccountInfo 接口,是用于 InMemoryAccountInfo。上传 URL 池仍然保存在内存中.
import { B2Client } from '@backblaze-labs/b2-sdk'
import { FileAccountInfo } from '@backblaze-labs/b2-sdk/auth/file'
const accountInfo = new FileAccountInfo('/var/cache/my-app/b2-auth.json', {
onDiscard: (event) => console.warn('ignored stale B2 auth cache', event.reason),
onWriteError: (event) => console.warn('could not persist B2 auth cache', event.error),
})
await accountInfo.load() // populate from disk if the file exists
const client = new B2Client({
applicationKeyId: process.env.B2_APPLICATION_KEY_ID,
applicationKey: process.env.B2_APPLICATION_KEY,
accountInfo,
})
if (accountInfo.getAuth() === null) {
await client.authorize() // first run, or token cleared
}
load() 悄悄返回丢失或损坏的文件(新鲜) authorize() 将包含新状态. onDiscard 当一个已装入的缓存项因域,应用程序密钥ID,或缓存端点与当前客户端不符而被忽略时点火; onWriteError 报告同步的写出故障,否则会做得最好。 打电话 await accountInfo.flushed() 如果您需要保证最新的状态已击中磁盘,则在进程退出前退出.
子路径导出
SDK被组织成树摇晃的子路径出口:
// High-level facade (most users need only this)
import { B2Client, Bucket, B2Object } from '@backblaze-labs/b2-sdk'
// Low-level 1:1 API bindings for the B2 native endpoints the SDK uses
import { RawClient } from '@backblaze-labs/b2-sdk/raw'
// Error types for catch blocks
import {
B2Error,
ExpiredAuthTokenError,
CapExceededError,
B2InsufficientCapabilityError,
} from '@backblaze-labs/b2-sdk/errors'
// Auth backends (in-memory default, file-backed for Node persistence)
import { InMemoryAccountInfo } from '@backblaze-labs/b2-sdk/auth'
import { FileAccountInfo } from '@backblaze-labs/b2-sdk/auth/file'
// Partner and Computer Backup API surfaces
import { PartnerClient, PartnerRawClient } from '@backblaze-labs/b2-sdk/partner'
import { BackupClient, BackupRawClient } from '@backblaze-labs/b2-sdk/backup'
// Streaming utilities + SSE-C key wrapper
import {
IncrementalSha1,
BufferSource,
BlobSource,
EncryptionKey,
} from '@backblaze-labs/b2-sdk/streams'
// Sync engine (local <-> B2)
import { synchronize, LocalFolder, B2Folder } from '@backblaze-labs/b2-sdk/sync'
// S3-compatible helpers
import {
createS3ClientConfig,
presignS3GetObjectUrl,
presignS3PutObjectUrl,
} from '@backblaze-labs/b2-sdk/s3'
// Webhook signature verification for B2 event notifications
import { verifyWebhookSignature, requireValidWebhook } from '@backblaze-labs/b2-sdk/notifications'
// In-memory B2 server for tests (no network required)
import { B2Simulator } from '@backblaze-labs/b2-sdk/simulator'
createS3ClientConfig() 用于 @aws-sdk/client-s3; 安装可选端点
在构建 AWS 之前 S3Client。签名前助手在内部签名和
不需要 AWS 预签包.
原始方法对请求控制使用线索选项包, 如取消
和按请求重试的覆盖:通过 { signal, retry } 改为 getUploadUrl, (中文(简体) ),
getUploadPartUrl, (中文(简体) ), uploadFile,以及 uploadPart. 较老的职位
signal, retry 表格仅用于源的相容性.
Raw 本地 API 版本策略
RawClient 构建 SDK 拥有的 B2 本地存储 URL /b2api/v4,匹配
当前发布的本地 API 文件 。 该版本集中在原始 URL 中
构建者路径; B2 返回的上传和下载 URL 用作返回.
Raw JSON 端点使用 POST 机构,包括读取/列出文件的操作
并显示 GET 查询实例, 如 b2_get_upload_url, (中文(简体) ), b2_get_file_info, (中文(简体) ),
b2_list_file_names,以及 b2_list_keysB2文档 POST JSON 兼容性
因此,SDK在移动时保持长期 POST 形状
路径版本到 v4 .
每个导出都以完整类型的签名记录在 API 参考。 。 。 .
同步过滤器
synchronize() 和内置的 LocalFolder 编号 : B2Folder 扫描仪接受 include 财务报告和审定财务报表 exclude 相对于每个同步根的路径过滤器 :
for await (const event of synchronize({
source: new LocalFolder('./site'),
dest: new B2Folder(bucket, 'site/'),
bucket,
prefix: 'site/',
options: {
compareMode: 'modtime',
keepMode: 'no-delete',
include: ['assets/**', '*.html'],
exclude: ['*.tmp', 'node_modules', 'dist/cache/**'],
},
})) {
console.log(event)
}
Glob 字符串使用 SDK 方言 : * 财务报告和审定财务报表 ? 在一个路径段内,整个 ** 区段交叉目录,无斜线图案匹配任何基名或祖先目录,排除胜出包含. 使用 dir/** 用于目录-子树模式,例如 dist/cache/**; 含有斜线的文字, 如 dist/cache 只匹配精确路径。 RegExp过滤器由最佳同步安全heuristic守护,它的确切子集可能会随着保护的收紧而改变. 当任何 RegExp 过滤器出现时, 超出 RegExp 输入守护的路径会被跳过, 包括排除- 唯一 RegExp 拒绝列表 。 B2 前缀是原始对象名前缀;后鞭不是重写到 /。 。 。 .
SyncFolder.scan() 直接消费者应期望有经过排序的路径 compareSyncRelativePaths从 @backblaze-labs/b2-sdk/sync期间 synchronize(),自定义文件夹在对齐之前进行过滤,即使在设定时也是如此 appliesScanFilters: true;扫描-侧过滤是一种优化,而不是政策边界. 自定义扫描器作者可以调用 filterSyncPaths(paths, options) 前进 SyncScanOptions.onSkip 因此,RegExp输入限制诊断被保留. 自定义未设置的文件夹 appliesScanSorting: true 在对齐前进行排序。 内置的本地扫描仪和B2扫描仪设置了这两面旗帜. SyncOptions.maxScanEntries 编号 : SyncScanOptions.maxScanEntries 默认为 SDK 扫描天花板, 超过时会因定义的扫描限制出错而失败; B2 扫描会计数所有列出的文件版本记录, 包括后来被前缀, 安全, 或过滤检查所跳过的版本, 而本地和倒置扫描会保留同步路径 。 过 Infinity 只有当过程堆积为完整结果集大小时; 提高极限会增加峰值扫描器内存, 因为内置扫描排序或组结果在生成前.
内置扫描器跳过诊断器 SyncSkipReason 诸如 outside-prefix, (中文(简体) ), unsafe-name, (中文(简体) ), local-unsafe-name, (中文(简体) ), relative-path-collision, (中文(简体) ), local-path-collision, (中文(简体) ), filesystem-error,以及 path-too-long-for-regexp编辑 onSkip 回调收到每个诊断 SyncEvent 流缓冲最初的100个扫描仪诊断,然后报告与 scan-skip-overflow。 。 。 .
B2- to- 本地同步跳过本地文件系统中不安全的对象名称, 包括 NTFS 替代数据流名称、 DOS 设备基名、 跟踪点/ 空间名称、 保留 .b2sdk-download-staging 名称空间,以及大小写/Unicode-canonical相撞。 在目标根管理下将流下载到私有的每下载条目 .b2sdk-download-staging 编目并公布已完成的文件; SDK 拥有的组装根和条目 .b2sdk-staging-marker.partial 文档。 局部扫描是只读的,当这些标记出现时,则排除了管理中的中转目录,因此废弃的中转状态不会被上传为源内容. 后来的下载设置只当标记和部分文件活动陈旧且在移除前保持不变时,才会收录已过时的SDK自有中转条目。 下载正文默认有60秒闲置超时; 设置 downloadIdleTimeoutMillis 调制它或 Infinity 以禁用监视器。 下载和本地删除拒绝在根下同位素本地根和同位素路径组件,然后在文件系统操作前立即重新验证根,父和叶的身份. 上传通过在扫描前将已解析的根身份绑定,然后重新打开无后继语义的被扫描文件并验证所打开的文件在读取前仍留在被绑定的源根内,从而支持对称链接的源根. 行动失败作为 error 事件; 终端聚合错误包括 failureCount,最多100个 failedPaths,以及 failedPathOmittedCount 当更多路径被忽略时.
自定义运输
SDK使用可插接的运输层. 默认 FetchTransport 使用本地语言 fetch API (英语). 你可以自己提供:
import type { HttpTransport, HttpRequest, HttpResponse } from '@backblaze-labs/b2-sdk'
class MyTransport implements HttpTransport {
async send(request: HttpRequest): Promise<HttpResponse> {
// your implementation
}
}
const client = new B2Client({
applicationKeyId: '...',
applicationKey: '...',
transport: new MyTransport(),
})
识别您的流量( 用户代理)
SDK问题的每个请求都包含一个用户代理头,Backflaze可以通过:
b2-sdk-typescript/<version-or-dev> (typescript; @backblaze-labs/b2-sdk; node/<node-version>; linux; x64)
两者 b2-sdk-typescript/ (稳定产品代币)和 @backblaze-labs/b2-sdk (npm package name)是有文件记载的合同的一部分——匹配其中之一的日志查询查找由这个SDK发出的每个请求. 评论块还报告运行时间(node/<version>, (中文(简体) ), bun/<version>, (中文(简体) ), deno/<version>,或 browser)外加非浏览器运行时间的OS和架构.
公众 VERSION 常数总是包成串。 User-Agent 产品令牌报告,仅从稳定的已公布的软件包中分解出;来源, CI, npm pack预发报告 b2-sdk-typescript/dev 因此,发展交通仍然可以区分.
要预置您的应用程序标识符 :
const client = new B2Client({
applicationKeyId,
applicationKey,
userAgent: 'my-app/1.0',
})
// → "my-app/1.0 b2-sdk-typescript/<version-or-dev> (typescript; @backblaze-labs/b2-sdk; node/<node-version>; linux; x64)"
安保部队警卫
默认 FetchTransport 拒绝任何主机位于授权的 B2 域外的 URL 的许可列表守护器。 这可以抵御URL-替代攻击,因为一个受损或敌对的B2端点可以返回一个指向内部服务的上传URL(例如:云元数据在 169.254.169.254并欺骗SDK 向它提出认证的请求.
const client = new B2Client({ applicationKeyId, applicationKey })
await client.authorize()
// Guard is now locked. Hosts under backblazeb2.com / backblaze.com are
// allowed; literal IPs, localhost, metadata.google.internal, *.internal,
// and *.local are rejected unconditionally; anything else throws B2SsrfError.
client.urlGuard?.getAllowedSuffixes()
// => ['backblaze.com', 'backblazeb2.com']
您可以扩展允许列表( 如调试时自动托管的 MITM 代理) , 而不会使守护者丧失功能 :
new B2Client({
applicationKeyId,
applicationKey,
allowedHostSuffixes: ['internal-proxy.example'],
})
通过 allowedHostSuffixes: [] 完全禁用警卫,并应当保留给可信的测试或控制局部用具。 对于自定义域, SDK 使用由 b2_authorize_account 作为范围后缀,允许这些主机及其子域不扩展未知域到公共后缀,例如 co.uk。 。 。 .
默认运输遵循同一来源 GET 编号 : HEAD 在每个目标通过SSRF后重新定向. 交叉来源的重新定向和 POST 重定向被屏蔽 B2RedirectError。通过 followSameOriginRedirects: false 页:1 B2Client 用来阻断同源重定向的构造器.
通过一个习惯 transport 选择退出警卫(你的运输,你的威胁模型).
重试行为
SDK 以指数后退自动重试瞬态错误 :
- 401 已过期 auth token: 重新授权和重试
- 408, 429, 瞬态 5xx (500, 502, 503, 504): 指数后退与焦急, 尊重
Retry-After标题 - 网络错误: 用后退重试
- 永久错误 (403个上限 已超过, 404个未找到等):立即抛出
配置重试行为 :
const client = new B2Client({
applicationKeyId: '...',
applicationKey: '...',
retry: {
maxRetries: 10,
maxRetryDelayMs: 120_000,
initialRetryDelayMs: 500,
requestTimeoutMs: 30 * 60_000,
},
})
requestTimeoutMs 覆盖请求发送、上传 POST 机构,以及
非流化反应体读取。 对于 response.body 下载流是一个
空闲/ 无进度超时, 在每个收到块后重排, 这样健康的大块
下载不只因为传输总量比
暂停。 缓慢的大段上传可能需要更高的值或 0 依赖
你们自己 AbortSignal最坏的病情终极期大概是
(retry.maxRetries + 1) * requestTimeoutMs 加上一个端点挂起时的后退.
当多段上传、流上传或多段副本失败时, SDK 调用 b2_cancel_large_file 在最佳的基础上。 过 onCleanupFailure 操作中发现失败的取消 或在模糊不清后跳出取消 b2_finish_large_file 相关答复 fileId 这样运算符就可以调和未完成或可能承诺的大型文件. 对应长期运行的恢复工作流程有生命周期或版本保留清理,因此未完成的大型文件不会累积到限定的恢复发现扫描后.
使用模拟器进行测试
SDK飞船装有用于单元测试的内模B2模拟器,没有网络接入:
import { describe, it, expect, beforeEach } from 'vitest'
import { B2Client, BucketType } from '@backblaze-labs/b2-sdk'
import { B2Simulator } from '@backblaze-labs/b2-sdk/simulator'
import { BufferSource } from '@backblaze-labs/b2-sdk/streams'
describe('my app', () => {
let client: B2Client
beforeEach(async () => {
const sim = new B2Simulator()
client = new B2Client({
applicationKeyId: 'test',
applicationKey: 'test',
transport: sim.transport(),
})
await client.authorize()
})
it('uploads and retrieves a file', async () => {
const bucket = await client.createBucket({
bucketName: 'test-bucket',
bucketType: BucketType.AllPrivate,
})
await bucket.upload({
fileName: 'test.txt',
source: new BufferSource(new TextEncoder().encode('hello')),
})
const listing = await bucket.listFileNames()
expect(listing.files).toHaveLength(1)
expect(listing.files[0].fileName).toBe('test.txt')
})
})
处理错误
所有 B2 API 出错都按键投出 B2Error 子类。 客户端能力检查 B2InsufficientCapabilityError。 。 。 .
import { BucketType } from '@backblaze-labs/b2-sdk'
import {
B2Error,
CapExceededError,
DuplicateBucketNameError,
B2InsufficientCapabilityError,
} from '@backblaze-labs/b2-sdk/errors'
try {
await client.createBucket({ bucketName: 'test', bucketType: BucketType.AllPrivate })
} catch (err) {
if (err instanceof DuplicateBucketNameError) {
console.log('Bucket already exists')
} else if (err instanceof CapExceededError) {
console.log('Storage cap exceeded, upgrade your plan')
} else if (err instanceof B2InsufficientCapabilityError) {
console.log('Missing capabilities:', err.missing)
} else if (err instanceof B2Error) {
console.log(`B2 error: ${err.code} (status ${err.status}, retryable: ${err.retryable})`)
}
}
B2-原生物,带有S3出逃舱口
高地平面(B2Client, (中文(简体) ), Bucket, (中文(简体) ), B2Object)让你直接访问B2的本地API中的功能:
- 每个部分和整个文件SHA-1核查 在多段上传时,当B2提供摘要时,在下载时加上自动全文件校验.
b2_copy_part服务器侧多部分副本 通过bucket.copyLargeFile()——没有客户端字节被触摸.- 文件保留+合法持有 (对象锁定)
bucket.updateFileRetention()财务报告和审定财务报表bucket.updateFileLegalHold()。 。 。 . - 时间范围下载符 通过
bucket.getDownloadAuthorization()用于在不披露应用程序密钥的情况下共享. - 复制配置 通过
bucket.update({ replicationConfiguration })。 。 。 . - 事件通知规则 通过
bucket.getNotificationRules()财务报告和审定财务报表bucket.setNotificationRules()。 。 。 . - 应用密钥限制 (每桶,每前缀,每容量)
client.createKey()。 。 。 .
当您想要S3兼容性时——用于已经说出S3的工具,浏览器直接上传,或者用于Bandwidth联盟代理模式—— @backblaze-labs/b2-sdk/s3 曝光情况 createS3ClientConfig(), (中文(简体) ), presignS3GetObjectUrl(),以及 presignS3PutObjectUrl() 所以同样的SDK覆盖了两个表面.
S3预先签名的URL是无记名证书. 援助者只发作 https: URL 和要求明确 region 区域不能来自 s3.<region>.backblazeb2.com; 设置 region 打电话 createS3ClientConfig() 在提供交通服务之前进行启动或部署健康检查。 完全带有路径段的对象密钥 . 或者说 .. 无法安全地指定 S3 , 因为常见的 URL 解析器在发送前会使其正常化; 重命名这些对象, 通过信任的服务器代理下载, 或者在符合访问模式时使用 B2- 本地下载授权 URL 。 URL有效性取决于签名主机的时钟,因此URL生成主机应该是NTP-synced,下游SigV4 403s应包括时钟-sew检查. 对于不信任的上传, 绑定 contentType 财务报告和审定财务报表 contentLength 当您可以时; 省略 contentType 允许上传器存储任何B2-接受类型,包括浏览器可执行的 HTML, SVG, XML, 或 JavaScript 。 除非可信任的服务器代码通过,否则拒绝连接活动内容类型 trustedUnsafeS3PresignOptIn 改为 allowBrowserExecutableContentType; 来自请求JSON的布尔被忽略. 获取响应覆盖强制内置渲染或活性内容要求其不安全的选入字段中的同物 。 SDK的活性内容检查是最佳的拒绝列表,而不是完整的浏览器安全政策;SDK不能设置 X-Content-Type-Options: nosniff,因此接受不可信内容的呼叫者应当执行自己的内容类型允许列表. 预先指定的PUT URL在到期前可以重放;如果客户端在B2存储对象后但在反应被观察之前重放,重试可以创建另一个文件版本. 使用唯一对象密钥或通过列表文件ID/checksum进行调和,并在必须自动去除重复版本的地方配置生命周期或版本清理.
堕落者 presignGetObjectUrl() 帮助者返回一个 B2- 本地下载授权 URL,而不是 S3 预先签名的 URL 。 它保持了遗留下来的斜向脱逃的URL形状并允许字符串构建行为相容. 使用 createNativeDownloadAuthorizationUrl() 用于新的 B2- 本地下载到的 URL; 它使用 B2 路径保存文件名编码, 需要一个 https: 后发光下载来源没有用户信息,路径,查询,或碎片,拒绝不安全的桶名,控制字符,无效相容持续时间,以及路径规范化的文件名,从解析出源构建无记名URL,而不是原始调用输入.
源异形论
SDK是异于常态的 来源 水平,不只是在已建成的文物水平。 每个内部进口都使用 .ts 扩展import { foo } from './foo.ts'没有 './foo.js', (中文(简体) ), tsconfig.json 已经 allowImportingTsExtensions: true + 键 rewriteRelativeImportExtensions: true,而 Vite 将扩展重写为 .js 在建筑过程中,npm消费者仍然看到正常 dist/。 。 。 .
这在实际中意味着什么: 你可以直接指向 src/ 没有建设步骤.
# Deno reads src/ directly. No `pnpm build`, no node_modules, no npm: shim.
deno check examples/node-list-buckets.ts
# Bun does the same.
bun examples/node-list-buckets.ts
# Node 22.6+ with --experimental-strip-types runs raw .ts.
node --experimental-strip-types examples/node-list-buckets.ts
所以你得到两个: 一个 npm install-准备好了 dist/ (ESM + CJS + DTS ), 财务报告和审定财务报表 (单位:千美元) src/ 在Node, Bun, 和Deno 运行的树,没有建筑。 在本地扩展 SDK 时有用, 贡献 PR , 或将消息源销售到 Deno 项目中 .
运行时间支持
| 运行时间 | 版本 | 状态 |
|---|---|---|
| 节点.js | 22.3+ (简体中文) | 首要目标。 CI在Linux,Windows,和macOS上运行快套房,用于节点22.22.2+24;在Linux上运行慢和覆盖的工作. FileSource 在Windows上使用更弱的大小/时间验证,因为可移植的dev/inode和ctime字段在那里是不可靠的. |
| 阿宝 | 最新消息 | 通过CI测试 bun test src/ + 示例类型检查. |
| 德诺 | 2.x 数据 | 源异构性通过CI验证 deno check 反对 src/。 。 。 . |
| 浏览器 | 铬、火狐、WebKit(最后2个常绿) | 通过Playwright在CI测试. |
| 云花岗工人 | - 说吧 | 具备上述标准Web API时可兼容的目标;尚未在CI中与工人运行时间进行烟雾测试. |
| Vercel 边缘 | - 说吧 | 当上面的标准Web API可用时,兼容目标;目前尚未与Vercel Edge在CI中进行烟雾测试. |
要求: fetch网络流, crypto.subtle, (中文(简体) ), AbortSignal. 节点 < 22.3不支持(第20个节点达到EOL 2026年4月). FileSource(path) 丢入缺少的运行时间 process.getBuiltinModule('node:fs')· ; FileSource.fromPath(path) 仍然是所支持的节点文件系统平台的同步构建路径。 在视窗上, FileSource 以更弱的大小/米时间验证支持,因为可移植的dev/inode和ctime字段不可靠.
浏览器测试套件( E)pnpm test:browser)对真实的Chrimium,Firefox和WebKit实例运行同一源. 只有节点特定测试( 文件名模式) *.node.test.ts,覆盖 node:fs, (中文(简体) ), node:os, (中文(简体) ), node:util.inspect)被跳过.
许可证
麻省理工学院. 见 自由主义。 。 。 .
捐款
见 捐款额 (c) 发展结构和准则.