# @12-apps/storage 中文文档

规范地址：https://npmcn.com/package/%4012-apps/storage/

版本：2\.9\.0
npm 元数据更新时间：2026-10-26T20:59:55.108Z
中文译文更新时间：2026-11-05T19:41:17.165Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/storage
原文对照：https://npmcn.com/package/%4012-apps/storage/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：200 kB，43 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.0\.0
- 运行依赖：2 个
- peer 依赖：hono \>=4\.0\.0，react \>=19\.0\.0，react-dom \>=19\.0\.0，@12-apps/wiring \>=1\.9\.0，@aws-sdk/client-s3 \>=3\.700\.0
- 首次发布：2026-08-13T08:04:45.993Z
- 最新版本发布：2026-10-26T20:59:54.429Z
- 近 30 天下载量：33530（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：13（近 12 个月 13 个）

## 中文简介

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

## 安装

```sh
npm install @12-apps/storage
```

也可以使用：`pnpm add @12-apps/storage`、`yarn add @12-apps/storage`、`bun add @12-apps/storage`

## README

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

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

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

> **收养这个?** 此 README 是 API 参考; **[`ADOPTING.md`](https://npmcn.com/package/%4012-apps/storage/ADOPTING.md)** 是分步骤的接线,包括五个所需的配置值和为什么 他们每个人都没有违约.

## 整个套装的规矩是围绕

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

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

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

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

## 子路径导出

| 导入 | 目录 |
| --- | --- |
| `@12-apps/storage` | Isomorphic core:格式允许列表,上传天花板,对象键语法,递解集,拒绝分类学,URL构建器,线条计. |
| `@12-apps/storage/server` | `createApiStorage(config)`,驱动端口,本地磁盘驱动端口,图像管端口兼有两种执行功能,可回收. |
| `@12-apps/storage/hono` | `storageRouter({ …config, resolveActor })` - 一通电话. `hono` 是一个可选的对等点. |
| `@12-apps/storage/s3` | `createS3Driver(config)` 每个S3兼容供应商. `@aws-sdk/client-s3` 是可选的对等方,只在这里解决. |
| `@12-apps/storage/react` | `createWebStorage({ 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 the``requests, and the next deploy discards both — with nothing in any log, because`写 File 每次都成功.

```ts
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 `??` 就在这里 因为一个丢失的物体 手提电脑不花钱:

```ts
// 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和用户代理发送到第三个 派对 没有上传,没有桶和司机参与,所以没有在存储器上 路径可以抓住它。 使用挂载本身的图谋 :

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

## 前端, 装满

```ts
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` 这里既非依附关系,也非对等关系: 模块.

```ts
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 是什么？

图片上传到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 和提交记录判断。

