@12-apps/storage

中文文档
2.9.0发布于 2026/10/27MITES Module构建来源已验证近 30 天下载 3.4万

图片上传到OHONGOUN, Isomorphic core (格式允许列表,上传天花板,对象键语法,递解集,拒绝分类学,线条计),主机挂后端表面 (./ server: Create ApiStorage, the l)

中文使用文档
文档目录10 节
  1. 整个套装的规矩是围绕
  2. 子路径导出
  3. 两个终点
  4. 这照片真多
  5. 无默认的五种值
  6. 后端, 装满
  7. 一个 imageKey 字段不是一个字符串字段
  8. 前端, 装满
  9. 图像管道是一个端口
  10. 第二家卖家是配置

@ 12- apps/ storage (简体中文)

图像上传降落在 您的原籍国从来没有在桶.

一个后端工厂,一个前端工厂,一个司机端口,两个 执行,以及图像管道端口,这样本地依赖性将保留 宿主的选择.

收养这个? 此 README 是 API 参考; ADOPTING.md 是分步骤的接线,包括五个所需的配置值和为什么 他们每个人都没有违约.

整个套装的规矩是围绕

浏览器 POST 将其字节输入应用服务器. 没有预先签名的URL 模式,而且决不能有一个模式.

预先签名的桶 URL 使工作上传取决于桶 (简体中文) 规则 ——既非此寄存器所居住,也非任何部署的配置 运行。 A 类 PUT 携带 Content-Type 从来不是一个"简单"的要求,所以 浏览器发送 OPTIONS 先飞行; 一个新创建的桶接应它 与 403 没有 Access-Control-Allow-* 信头,上传从不 发送。 是什么使得失败模式特别糟糕:

  • 服务器看不到任何东西. 没有请求到来——没有日志行,没有出错 利率,没有5xx.
  • 浏览器不会说原因 . 一个被封锁的跨源取回是故意的 无法与离线网络区分 fetch 层.
  • 所见之人不能修. 消息传到店主身上, 只有操作员才能配置一个桶.

无法返回的第二个原因: 存储的照片是 设定 对象 服务器来自字节,所以上传服务器从未看到无法生成 一个.

子路径导出

导入目录
@12-apps/storageIsomorphic core:格式允许列表,上传天花板,对象键语法,递解集,拒绝分类学,URL构建器,线条计.
@12-apps/storage/servercreateApiStorage(config),驱动端口,本地磁盘驱动端口,图像管端口兼有两种执行功能,可回收.
@12-apps/storage/honostorageRouter({ …config, resolveActor }) - 一通电话. hono 是一个可选的对等点.
@12-apps/storage/s3createS3Driver(config) 每个S3兼容供应商. @aws-sdk/client-s3 是可选的对等方,只在这里解决.
@12-apps/storage/reactcreateWebStorage({ apiBase }) ——绑定取取器,预览取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取.

出口地图位于 ./src 直接——在 运行时间.

两个终点

POST <mount>/uploads/image      raw bytes in, { data: { imageKey } } out
GET  <mount>/uploads/local/…    the object back, or a redirect to where it lives

服务路线有两个行为 驱动程序 决定: 驱动程序 保留自己的字节执行器 read 和路线 桶后驱动程序没有, 路径会重新定向到 publicUrl时使用 成为路由内部读取的环境旗——同样的事实在一秒之内 位置,可以对实际使用的驱动程序有异议.

这照片真多

products/<scope>/<uuid>/full.webp        the whole picture — the zoom
products/<scope>/<uuid>/card-320.webp    4:3, a card on a phone
products/<scope>/<uuid>/card-640.webp    4:3, a retina card / a phone hero
products/<scope>/<uuid>/card-1280.webp   4:3, a retina hero on a desktop
products/<scope>/<uuid>/thumb-128.webp   1:1, a 64px row thumbnail
products/<scope>/<uuid>/thumb-256.webp   1:1, the same row at 2×

在行上保存的密钥名称为未剪接的成员,以及 键的形状是 作物是怎样存在的数字 : …/<uuid>/full.<ext> 有他们,公寓 …/<uuid>.<ext> 没有。 没有其他记录——没有栏,因此 草案、变更请求、回收站 快照和一行的版本历史被复制成.

<scope> 是租户。 就是它使得交叉租户重新找回一个剖面 而非查询: 回收时拒绝使用范围不是呼叫者的密钥 在此之前 咨询任何参考探测器。 没有它的计划只能回答"也许" 租户删除此对象。” 他们的 点数 对于其他每个房客的客体来说也是.

无默认的五种值

createApiStorage 需求 driver, (中文(简体) ), maxBytes, (中文(简体) ), imagePipeline, (中文(简体) ), unscopedKeys 财务报告和审定财务报表 references。每个安全值都是针对主机的,所以默认值 由谁打出第一个主机 默默继承 以它之后的每个人盟誓 安静,这是实际 参数。 第一次部署时会修正一个响亮的默认值;这些不:

配置违约要花多少钱
driver一个容器的麻黄磁盘在生产中,每一次上传都报告成功,并在下一次部署时丢失了物体.
maxBytes不管这个文件有多少, 在一个主机中,它的工具计划广告其他的东西.
imagePipeline作物是否存在——从生产中钥匙的形状中发现.
unscopedKeys一个租户的开垦能否达到另一个租户的字节.
references恢复的行是否还有相片 . [] 不是中立的:这意味着 立即归还一切。 。 。 .

logger 继续违约,这是合法可以做到的: 只有报告,永远不要增加,所以最糟糕的是默认的伐木者成本是沉默. references 也默认,直到它没有——因为它的默认 销毁。 。 。 .

后端, 装满

注意没有 ??。 。 。 . 以下每个值均来自 环境,必须在那里,或者被写出来。 这是故意的:a process.env.UPLOADS_DIR ?? \${ process.cwd ()}/. uploads' (中文(简体) )in this position is exactly the fail-open default the table above refuses to have.UPROADS DIR 导弹unset in production means uploads succeed, one container holds the object, the other 404s half therequests, and the next deploy discards both — with nothing in any log, because写 File 每次都成功.

import { createApiStorage } from '@12-apps/storage/server';
import { createLocalDiskDriver, passthroughImagePipeline } from '@12-apps/storage/server';
import { DEFAULT_MAX_UPLOAD_BYTES, STORAGE_PATHS } from '@12-apps/storage';

/** Fail at BOOT for a missing value, rather than at the next deploy. */
function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`${name} is required — it must be a MOUNTED VOLUME, not a container path.`);
  return value;
}

const storage = createApiStorage({
  driver: createLocalDiskDriver({
    root: requireEnv('UPLOADS_DIR'),
    publicPathPrefix: `/api${STORAGE_PATHS.serve}`,
  }),
  maxBytes: DEFAULT_MAX_UPLOAD_BYTES,
  imagePipeline: passthroughImagePipeline(),
  // 'reject' because this host is fresh: every key it will ever mint is scoped, so
  // a key without a scope can only be someone else's or nobody's. A host with rows
  // that predate the scope segment passes 'accept' — see ADOPTING.md rule 2.
  unscopedKeys: 'reject',
  // Which of YOUR tables can still need an object after the row stopped pointing at
  // it. `[]` says "nothing here copies a key" — write it only if you have checked.
  references: [
    { name: 'live-rows', referenced: (scope, key) => rowPointsAt(scope, key) },
  ],
});

对于 发展 页:1 ?? 就在这里 因为一个丢失的物体 手提电脑不花钱:

// DEV ONLY. Never in the block above.
root: process.env.UPLOADS_DIR ?? `${process.cwd()}/.uploads`,

storage 携带一个主机所需的一切:

成员使用
routes框架中立说明. @12-apps/storage/hono 让他们上船.
limits这个挂载所执行的——从它中提供工具计划,从不从你自己的常数中提供.
schemas.inlineImage一个Zod计划,它的基数64上限 实值 maxBytes。 。 。 .
schemas.objectKey计划一个 imageKey 字段. 没有 z.string() - 见下文.
storeImage 编号 : storeInlineImage包含字节的宿主写入的写入路径(一个代理的唯一选项).
urls.objectUrl 编号 : urls.imageSources 编号 : urls.versionedObjectUrl键 = URL,通过活动驱动程序.
reclaim.*重取一个被替换的对象、 被替换的集或密钥, 仅刻出一个失败的写入 .

一个 imageKey 字段不是一个字符串字段

objectUrl 返回已存在的绝对值 不变 ——深思熟虑 支付从 URL 列移出的主机的费用 。 所以一个接受 imageKey: z.string() 接受 https://tracker.example/p.png,每个买家 装入存储首页后将IP和用户代理发送到第三个 派对 没有上传,没有桶和司机参与,所以没有在存储器上 路径可以抓住它。 使用挂载本身的图谋 :

const body = z.object({
  imageKey: storage.schemas.objectKey.nullish(),
  image: storage.schemas.inlineImage.optional(),
}).superRefine(refineImageInput);

前端, 装满

import { createWebStorage } from '@12-apps/storage/react';

const storage = createWebStorage({ apiBase: '/api' });
// storage.page        a standalone upload surface
// storage.ImageField  the field a form drops in — a key comes out
// storage.useUpload   the primitive, for a bespoke affordance

图像管道是一个端口

图像处理是指原生模块,原生模块是构建地表 每个消费者都会为每次部署付出代价 不论他们是否想要作物 这么说吧 sharp 这里既非依附关系,也非对等关系: 模块.

import sharp from 'sharp';
import { createSharpImagePipeline } from '@12-apps/storage/server';

imagePipeline: createSharpImagePipeline({ sharp });

passthroughImagePipeline() 是另一个选择: 字节按原样存储 没有庄稼,没有扁平的钥匙, 这正是每个目录的图像 在庄稼存在之前.

神奇数字检查不是管道的工作,这个读取器曾经说 这是。 它属于WRITER: storeImage 校正字节对齐 在任何管道看到它们之前,宣布类型为第一个声明。 那个 安排是故意的,因为不能同时相信这两项执行 带着它.. passthrough 核实为无所事事的一部分,同时 sharp 无法: "did libvips decode this"和"这就是被宣布的格式"是 不同的问题,且 libvips 解码了大量的允许列表格式 除外。 把支票放进写作机里就意味着没有路可走 输入的管道 以及字节是否到达 结束点, 写作中的基数64, 或者通过直接 storeImage 电话.

第二家卖家是配置

createS3Driver 覆盖每个S3兼容的商店. AWS需要一个 region· ; 数字海洋空间,MinIO,R2和其余需要 endpoint附加时 forcePathStyle 给那些不能虚拟地址的人 没有 上传路径中的分支更改,没有写入主机代码.

包里没有写 process.env。 。 。 .

版本与发布记录

@12-apps/storage 共发布 13 个版本,首次发布于 2026/08/13;过去 12 个月发布了 13 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.9.0latest2026/10/2711 天前
2.8.02026/08/252 个月前
2.7.02026/08/252 个月前
2.6.12026/08/252 个月前
2.6.02026/08/242 个月前
2.5.02026/08/242 个月前

关于 @12-apps/storage 的常见问题

@12-apps/storage 是什么?

图片上传到OHONGOUN, Isomorphic core (格式允许列表,上传天花板,对象键语法,递解集,拒绝分类学,线条计),主机挂后端表面 (./ server: Create ApiStorage, the l)

如何安装 @12-apps/storage?

在项目目录运行 npm install @12-apps/storage,或使用 pnpm add / yarn add / bun add。

@12-apps/storage 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/12-apps__storage 是否存在。

@12-apps/storage 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@12-apps/storage 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@12-apps/storage 可以商用吗?

@12-apps/storage 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@12-apps/storage 的下载量有多少?

根据 npm 官方下载统计,@12-apps/storage 在 2026/10/05 – 2026/11/03 这 30 天内被下载 33,530 次,日均约 1,118 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@12-apps/storage 还在维护吗?

最新版本 2.9.0 发布于 2026/10/27(11 天前);至今共发布 13 个版本,其中过去 12 个月发布了 13 个;首次发布于 2026/08/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。