数据视图中的操作:将图像添加到媒体库
距离《在插件中使用 Data Views 显示和交互数据》发布已近一个月。在那篇文章中,你学习了如何使用 Data Views 创建一个在 WordPress 管理后台显示 React 应用、列出图片数据集的插件。通过 DataViews 组件,你能够显示图像数据集,并为用户提供良好的界面来显示、排序、搜索和筛选照片列表。
在那篇文章中,你学习了如何:
- 向管理屏幕添加自定义 React 应用。
- 利用 DataViews 组件显示数据集。
现在用户界面已准备就绪,用户可以显示、排序、搜索和筛选图片列表,是时候进一步推进该项目的 Data Views 操作,为用户提供工具,将任何列出的图片直接添加到媒体库。
在本文中,你将构建:
- 使用户能够将选定图片直接上传到媒体库的操作。
- 打开包含中间对话框以启动特定操作的模态窗口的操作。
- 提供实时反馈的用户友好界面,让用户了解正在执行的进程。
在奠定了使用 Data Views 的基础之后,本文探讨了你可以用它们执行的操作。这包括通过 REST API 与 WordPress 媒体交互、创建通知框、显示模态窗口以及使用其他 UI 组件。
开始之前
在本文中,你将基于上一篇文章开始的项目继续构建,从上次停止的地方继续。
要获取本文的起始点项目,可以执行以下操作:
git clone git@github.com:wptrainingteam/devblog-dataviews-plugin.git
git checkout part1
本文解释的项目的最终代码可在 GitHub 上获取。在整篇文章中,你会找到指向特定提交的链接,这些链接对应正在解释的更改,以帮助你跟踪项目进度。
将图片添加到媒体库的操作
在目前状态下,该项目只有一个操作来全尺寸显示图片。现在让我们创建一个新操作来将图片上传到媒体库。
将以下代码添加到传递给 Dataviews 组件的 actions 属性的 actions 数组中:
const actions = [
{
id: 'upload-media',
label: __( 'Upload Media' ),
isPrimary: true,
icon: 'upload',
supportsBulk: true,
callback: ( images ) => {
images.forEach( ( image ) => {
console.log( `Image to upload: ${ image.slug }` );
});
},
},
...
]
这个新操作启用了 Data Views UI 中的多选功能。目前,当触发时,它会为每个选定的图片向控制台记录一条消息。
请记住运行 <a href="https://docs.npmjs.com/cli/v10/commands/npm-start">npm start</a>(它是 npm run start 的别名),以便在文件更改时自动生成项目的构建版本。
‘see-original’ 操作是使用属性 id、label 和 callback 定义的。如上所示,这个新的 ‘upload-media’ 操作的定义使用了一些额外的属性,例如:
isPrimary– 将此属性设置为true将使此操作成为每个项目的默认操作。supportsBulk– 通过将此属性设置为 `true`,你可以启用项目的多选。启用此设置后,你可以同时对多个项目执行操作。icon– 为主要操作显示的图标。
请注意,此操作的回调函数现在接收一个或多个项目的数组。通过使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">forEach</a>,你可以对每个选定的项目执行一些逻辑。
请在此处查看可用于定义 Data Views 项目操作的属性完整列表。
现在 ‘upload-media’ 操作已创建,让我们花点时间考虑给定 URL 将图片上传到媒体库所需的步骤:
- 从提供的 URL 下载图片并将其转换为 blob。
- 创建一个包含图片 blob 的 FormData 对象,该对象可以作为 POST 请求的正文发送。
- 使用
<a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/">apiFetch</a>向 WordPress REST API 的适当端点发送 POST 请求,以将图片添加到媒体库。
基本上,你需要向特定的 REST API URL 发出 POST 请求,其中包含二进制格式的图片数据。为此,需要一些预备步骤来为 REST API 请求准备数据。
将这些步骤作为注释添加到 forEach 回调中,以便在我们为每个步骤添加逻辑时作为参考:
const actions = [
{
id: 'upload-media',
...
callback: ( images ) => {
images.forEach( ( image ) => {
// 1- Download the image and convert it to a blob
// 2- Create FormData with the image blob
// 3- Send the request to the WP REST API
});
},
},
...
]
下载图片并将其转换为 blob
每个项目的图片 URL 可在 image.urls.raw 处获取,因此你可以使用浏览器的原生 fetch 方法下载每个图片,并将响应转换为 blob。
要在 POST 请求中发送图片,需要将其转换为可以通过网络传输的格式。一种常见的格式是二进制大对象 (Blob)。
将此代码添加到 forEach 的回调函数中:
images.forEach( async ( image ) => {
// 1- Download the image and convert it to a blob
const responseRequestImage = await fetch( image.urls.raw );
const blobImage = await responseRequestImage.blob();
...
})
由于 fetch 是一个返回 Promise 的异步方法,你可以将 forEach 回调声明为一个 async 函数。这允许你使用 await 运算符,并避免使用 Promise 链来处理 Promise 响应。
fetch 的成功响应封装在一个 Response 实例中,该实例包含一个 blob() 方法。此方法允许你直接从响应生成 blob。
使用图片 blob 创建 FormData
图片以 blob 格式准备好后,下一步是准备要发送到 REST API 的 HTTP 请求中的数据。在 POST 请求中发送数据的一种方法是使用 FormData 对象作为请求正文。
像 fetch(或 WordPress 的 apiFetch)这样的方法可以使用 FormData 对象作为请求的正文。此数据被编码并以 Content-Type 设置为 multipart/form-data 的方式传输。
将此代码添加到 forEach 的回调函数中:
<
images.forEach( async ( image ) => {
...
// 2- Create FormData with the image blob
const formDataWithImage = new FormData();
formDataWithImage.append(
'file',
blobImage,
`${ image.slug }.jpg`
);
...
})
上面的代码创建了一个新的 FormData 对象,附加了一个包含 blob 格式图片的 file 字段。
<a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData">FormData</a> 对象表示 HTML 表单数据。从服务器的角度来看,就像你提交了来自 HTML 表单的数据。
向 WP REST API 发送请求
此时,一切准备就绪,可以使用 apiFetch 方法进行 REST API 请求。
<a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/">apiFetch</a> 是 window.fetch 的包装器,在向 WP REST API 发出请求时提供了几个优势,例如自动完成 REST API 端点的基本 URL,并在请求头中包含 nonce。
转到 App.js 的开头,从 @wordpress/api-fetch 导入 apiFetch 方法:
import apiFetch from "@wordpress/api-fetch";
现在将此代码添加到 forEach 的回调函数中:
images.forEach( async ( image ) => {
...
// 3- Send the request to the WP REST API with apiFetch
await apiFetch({
path: "/wp/v2/media",
method: "POST",
body: formDataWithImage,
})
.then( console.log )
...
})
上面的代码使用 apiFetch 向 wp/v2/media 端点发出 (POST) 请求。如 REST API 手册中所述,你可以通过向 /wp/v2/media 端点发送 POST 请求在 WordPress 安装中创建媒体项目。
公共 REST API 自 WP 4.7 起已成为核心的一部分。
你的 Data Views 现在包含一项新功能,允许你将显示的照片直接上传到媒体库。你可以单独上传图片,也可以选择多个图片进行批量上传。

然而,用户获得的关于上传进程的反馈并不理想,因为它只提供控制台消息。让我们努力改善这种用户体验。
显示上传进程结果的通知框
WordPress 提供了一个通知系统,可以通过通知存储进行管理。与通知 UI 交互的一个好方法是用 withNotices 高阶组件包装你的 React 组件。
通过用 withNotices 包装你的 App 组件,你的组件将收到额外的属性 noticeOperations 和 noticeUI:
noticeOperations是一个对象,包含createNotice方法(以及其他方法),你可以使用它来生成特定的通知框。noticeUI是 Notice React 组件,当创建通知框时,它将被显示。
转到 App.js 的开头,从 "@wordpress/components" 导入 withNotices 方法:
import { withNotices } from "@wordpress/components";
转到定义 App 组件开始的那一行,将其替换为以下代码:
const App = withNotices(({ noticeOperations, noticeUI }) => {
const { createNotice } = noticeOperations;
...
});
你可以使用从 noticeOperations 解构的 createNotice 方法来创建通知框,这些通知框将出现在你在 React 应用屏幕上放置 noticeUI 组件的任何位置。这些通知框可用于向用户提供有关图片上传到媒体库成功或失败的信息。
将以下 then 和 catch 方法链式调用到 apiFetch 调用,以处理 API 请求的成功或错误,使用尚未定义的 onSuccessMediaUpload 和 onErrorMediaUpload 函数:
// 3- Send the request to the WP REST API with apiFetch
await apiFetch({
path: "/wp/v2/media",
method: "POST",
body: formDataWithImage,
})
.then(onSuccessMediaUpload)
.catch(onErrorMediaUpload);
现在,将以下代码添加到 App 组件中(并在调用这些函数之前),其中包含 onSuccessMediaUpload 和 onErrorMediaUpload 函数的定义:
const onSuccessMediaUpload = (oImageUploaded) => {
const title = oImageUploaded.title.rendered;
createNotice({
status: "success",
content: __(`${title}.jpg successfully uploaded to Media Library!`),
isDismissible: true,
});
};
const onErrorMediaUpload = (error) => {
console.log(error);
createNotice({
status: "error",
content: __("An error occurred!"),
isDismissible: true,
});
};
onSuccessMediaUpload 接收由 /wp/v2/media 端点返回的对象,其中包含成功上传的图片的详细信息。利用这些信息,你可以生成一个 success 框,通知用户图片上传成功。
如果上传操作失败,onErrorMediaUpload 将收到导致上传操作失败的具体错误。在此函数内部,你还可以调用一个 error 框来通知用户出现了问题。
要显示由 createNotice 方法触发的通知框,将 noticeUI 组件添加到 App 组件的返回中:
return (
<>
{noticeUI}
<DataViews
...
/>
</>
);
React 组件只能返回一个父元素,因此你可以使用 `Fragment` 组件将你的元素分组在一起。
由于通知框将出现在屏幕顶部,你可以在 ‘upload-media’ 回调的开头添加以下代码,以便每次触发此操作时滚动到顶部,确保用户不会错过任何通知:
window.scrollTo( 0, 0 );