REST API: 上传文件
页面摘要:
/api/uploadREST API 端点使你可以将文件上传到 Media Library(媒体库)、检索分页的文件列表、更新文件元数据,并从你的 Strapi 应用中删除文件。
Media Library 功能在 Strapi 的后端服务端中由 upload 包提供支持。要将文件上传到 Strapi,你可以直接在 admin panel(管理面板)中使用 Media Library,也可以使用 REST API,其可用端点如下:
| 方法(Method) | 路径(Path) | 描述(Description) |
|---|---|---|
| GET | /api/upload/files | 获取文件列表 |
| GET | /api/upload/files/page | 获取分页的文件列表 |
| GET | /api/upload/files/:id | 获取特定文件 |
| POST | /api/upload | 上传文件 |
| POST | /api/upload?id=x | 更新 fileInfo(文件信息) |
| DELETE | /api/upload/files/:id | 删除文件 |
- Folders(文件夹) 是一个仅限 admin panel(管理面板)的功能,不属于 Content API(REST 或 GraphQL)。通过 REST 上传的文件会被放置在自动创建的 "API Uploads" 文件夹中。
- GraphQL API 不支持上传媒体文件。要上传文件,请使用 REST API,或直接在 admin panel(管理面板)中的 Media Library 添加文件。某些用于更新或删除已上传媒体文件的 GraphQL mutation 仍然是可行的(详见 GraphQL API 文档)。
Get a list of files
2 个端点会从 Media Library 返回文件:/api/upload/files 以扁平数组的形式返回所有文件,而 /api/upload/files/page 则使用标准的分页响应来返回文件。对于任何具有一定规模的 Media Library,都建议使用 /api/upload/files/page。
Get all files
GET /api/upload/files 会返回 Media Library 中所有文件的扁平数组:
[
{
"id": 1,
"documentId": "a1b2c3...",
"name": "photo.jpg",
"url": "/uploads/photo.jpg",
"mime": "image/jpeg",
"size": 12.34
// ...其它文件字段
}
// ...
]
该端点会忽略分页参数,并始终返回所有文件。对于大型媒体库,响应可能会非常庞大,因此请优先使用 /api/upload/files/page。
Get a paginated list of files
GET /api/upload/files/page 使用标准的分页响应返回文件,并包裹在 data 数组和 meta.pagination 对象中。
它接受与其它 REST collection(集合)端点相同的查询参数:
| 参数(Parameter) | 描述(Description) |
|---|---|
pagination[page] | 页码(从 1 开始)。默认值为 1。 |
pagination[pageSize] | 每页文件数。默认为 api.rest.defaultLimit 配置(25),若设置了 api.rest.maxLimit 则以该值为上限。 |
pagination[start] | 基于偏移量的分页:要跳过的文件数。 |
pagination[limit] | 基于偏移量的分页:要返回的最大文件数。 |
pagination[withCount] | 是否运行 count 查询并在响应中包含 total 和 pageCount。默认为 true。 |
filters | 对结果进行过滤。 |
sort | 对结果进行排序。 |
fields | 选择要返回哪些字段。 |
populate | 联表加载关联。 |
分页参数使用嵌套格式(pagination[page]=2&pagination[pageSize]=10),而非扁平格式(page=2&pageSize=10)。基于页码的分页(page/pageSize)与基于偏移量的分页(start/limit)是互斥的:将它们组合使用会返回 400 错误。关于这两种分页方式的详细说明,请参阅 Sort & Pagination(排序与分页)。
请求:Example request: Get the second page of 10 files
GET /api/upload/files/page?pagination[page]=2&pagination[pageSize]=10
响应
{
"data": [
{
"id": 1,
"documentId": "a1b2c3...",
"name": "photo.jpg",
"url": "/uploads/photo.jpg",
"mime": "image/jpeg",
"size": 12.34
// ...其它文件字段
}
],
"meta": {
"pagination": {
"page": 2,
"pageSize": 10,
"pageCount": 4,
"total": 100
}
}
}
当使用基于偏移量的分页时,meta.pagination 对象会返回 start 和 limit,而不是 page 和 pageSize:
请求:Example request: Get 5 image files, skipping the first 20, without a total count
GET /api/upload/files/page?pagination[start]=20&pagination[limit]=5&pagination[withCount]=false&filters[mime][$startsWith]=image/
响应
{
"data": [
// ...
],
"meta": {
"pagination": {
"start": 20,
"limit": 5
}
}
}
当 pagination[withCount] 为 false 时,count 查询会被跳过,total 和 pageCount 也会从响应中省略。
Upload files
向你的应用上传一个或多个文件。
files 是唯一被接受的参数,用于描述要上传的文件。其值可以是 Buffer 或 Stream。
当 AWS S3 的 ACL 参数设置为 "private" 时,上传端点返回的文件 URL 会自动进行签名。签名 URL 包含 X-Amz-Signature 查询参数,并在响应中带有 isUrlSigned: true 标志,即便在私有存储桶 ACL 下也能访问这些 URL。签名 URL 的过期时间取决于你的 signedUrlExpires 配置(默认:15 分钟)。
上传图片时,请包含一个 fileInfo 对象来设置文件名、替代文本(alt text)和说明文字(caption)。
Browser
<form>
<!-- Can be multiple files -->
<input type="file" name="files" />
<input
type="hidden"
name="fileInfo"
value='{"name":"homepage-hero","alternativeText":"Person smiling while
holding laptop","caption":"Hero image used on the homepage"}'
/>
<input type="submit" value="Submit" />
</form>
<script type="text/javascript">
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
await fetch('/api/upload', {
method: 'post',
body: new FormData(e.target)
});
});
</script>
Node.js
import { FormData } from 'formdata-node';
import fetch, { blobFrom } from 'node-fetch';
const file = await blobFrom('./1.png', 'image/png');
const form = new FormData();
form.append('files', file, "1.png");
form.append(
'fileInfo',
JSON.stringify({
name: 'Homepage hero',
alternativeText: 'Person smiling while holding laptop',
caption: 'Hero image used on the homepage',
})
);
const response = await fetch('http://localhost:1337/api/upload', {
method: 'post',
body: form,
});
你必须在请求体中发送 FormData。
Upload entry files
上传将与某个特定条目相关联的一个或多个文件。
接受以下参数:
| 参数(Parameter) | 描述(Description) |
|---|---|
files | 要上传的文件。其值可以是 Buffer 或 Stream。 |
path(可选) | 文件将被上传到的文件夹(仅 strapi-provider-upload-aws-s3 支持)。 |
refId | 文件将被关联到的条目的 ID。 |
ref | 文件将被关联到的模型(model)的唯一 ID(uid)(详见下文)。 |
source(可选) | 模型所在插件的名称。 |
field | 文件将被精确关联到的条目的字段。 |
例如,给定 Restaurant 模型的属性:
{
// ...
"attributes": {
"name": {
"type": "string"
},
"cover": {
"type": "media",
"multiple": false,
}
}
// ...
}
以下是一个对应的前端代码示例:
<form>
<!-- Can be multiple files if you setup "collection" instead of "model" -->
<input type="file" name="files" />
<input type="text" name="ref" value="api::restaurant.restaurant" />
<input type="text" name="refId" value="5c126648c7415f0c0ef1bccd" />
<input type="text" name="field" value="cover" />
<input type="submit" value="Submit" />
</form>
<script type="text/javascript">
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
await fetch('/api/upload', {
method: 'post',
body: new FormData(e.target)
});
});
</script>
你必须在请求体中发送 FormData。
Update fileInfo
更新你的应用中的一个文件。
fileInfo 是唯一被接受的参数,用于描述要更新的 fileInfo(文件信息):
import { FormData } from 'formdata-node';
import fetch from 'node-fetch';
const fileId = 50;
const newFileData = {
alternativeText: 'My new alternative text for this image!',
};
const form = new FormData();
form.append('fileInfo', JSON.stringify(newFileData));
const response = await fetch(`http://localhost:1337/api/upload?id=${fileId}`, {
method: 'post',
body: form,
});
Models definition
向模型(或另一个插件的模型)添加文件属性,类似于添加一个新的关联。
以下示例允许你上传并将一个文件附加到 avatar 属性:
{
// ...
{
"attributes": {
"pseudo": {
"type": "string",
"required": true
},
"email": {
"type": "email",
"required": true,
"unique": true
},
"avatar": {
"type": "media",
"multiple": false,
}
}
}
// ...
}
以下示例允许你上传并将多张图片附加到 restaurant 内容类型:
{
// ...
{
"attributes": {
"name": {
"type": "string",
"required": true
},
"covers": {
"type": "media",
"multiple": true,
}
}
}
// ...
}