基本介绍 ?
- 最近完成了产品提出的一个类似微信发图文朋友圈的需求(以下实现均基于 React + mobx 项目):
- 1⃣️ 通过➕号按钮选择系统图片,最多可选择9张图,选择的图片以九宫格形式展示
- 2⃣️ 选择图片后开始上传,图片大小限制为 20 MB,超过限制不上传且不展示
- 3⃣️ 展示图片上传过程中的 loading 状态
- 4⃣️ 图片上传失败可点重试按钮重新上传
- 5⃣️ 可以删除选中的图片(若上传中删除前还需中断 xhr 请求)
- 6⃣️ 发布后的图片内容按照一定规则展示(单图片、四宫格和九宫格)
- 历尽千辛万苦终于实现了以上的功能,具体效果如下:
- 上传图片页面:
- 展示页面(单图,按照单图规则展示):
- 展示页面(四图,四宫格):
- 展示页面(多图,九宫格):
- 该需求涉及到许多零散的知识点,遂以此文做总结记录,下面将介绍思路和具体的实现代码。
思路分析 ?
选择图片
- 选择图片可以用设置
type
属性为file
值的input
标签:
<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" multiple />
- 通过设置其
multiple
属性和accept
属性可以使得input
标签支持多选和仅支持选择gif
、jpg/jpeg
和png
类型的文件。
- 当点击标签时,则可以从文件系统中选择照片。我们的需求是提供一个➕号按钮作为选择图片的入口,这里可以使用
ref
获取该input
元素,当点击➕号按钮时触发input
的点击事件,即可以调用系统的相册进行图片选取。
限制图片大小和数量、预览本地图片
- 当
input
元素的change
事件被触发时,可以通过input.files
获取到当前选中文件的 FileList 类数组对象,当选择两张图片后,打印FileList
对象: - 可以看到每个 File 对象都有 size 字段,可以用来判断图片大小(字节)是否超过限制(
20MB
是20 * 1024 * 1024
)。 - 区别于原生 APP,web 网页无法阻止用户选择超过 9 张图片,只能在代码中做限制,当总选择的图片超过 9 张则做超出数量提示和截断处理(取前 9 张图)。
- 由于可以获取到每张图片的
File
对象,则可以使用URL.createObjectURL()
创建一个对象 URL,可以作为img
标签的src
值进行传入,则能实现本地图片的预览功能。 - 需要注意的是,当不再需要这些使用
URL.createObjectURL()
创建的 URL 对象时,每个对象必须通过调用URL.revokeObjectURL()
方法来释放,让浏览器知道不用在内存中继续保留对这个文件的引用了(可设置图片的load事件处理器来释放对象URL,当图片加载完成之后对象URL就不再需要了)。const localUrl = URL.createObjectURL(flieList[0]) // localUrl 可作为图片的源 <img src={localUrl} alt='' /> // 无需使用时释放内存 window.URL.revokeObjectURL(localUrl);
上传图片和失败重试
- 通过前面的分析,我们可以获取到每张图片的
File
对象,我们就可以通过新建一个FromData
对象,并将File
对象添加到FromData
对象中,使用XMLHttpRequest
来处理无刷新上传图片。const xhr = new XMLHttpRequest(); const formData = new FormData(); formData.append('file', fileData); xhr.open('POST', UPLOAD_IMAGE_URL); // 需要后端提供上传 URL xhr.send(formData); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200) { // handle response } else { // handle error } } }
- 上传失败时则重新执行以上上传逻辑。
图片展示
- 发布后的图片需要按照一定的规则展示:
- 单张图使用微信朋友圈图片的显示规则,需要获取图片原本的宽高,因此需要后端在上传图片完成后返回图片的原始宽高。
- 四张图时使用四宫格
- 其他数量的图片以九宫格形式展示
- 为了使得图片在保持其宽高比的同时填充
img
元素的整个内容框,我们通过可以设置img
元素的 object-fit CSS 属性值为cover
来实现。 - 下面将介绍整个图片上传和展示功能的具体实现代码。
具体实现 ?
上传图片页面相关组件
-
这部分将实现创建页的上传图片组件,当添加图片张数少于9张时展示➕号按钮,图片添加后开始上传图片,图片上传过程中展示 loading 状态,添加的图片可以被删除,即效果如下:
-
首先是
UploadImage
组件,代码如下: -
UploadImage
组件中使用了ImageItem
子组件,封装了上传、删除、重试逻辑,相关代码如下: -
样式文件代码如下:
-
可以看到,在使用
UploadImage
组件时需要传入imageList
和及其更新方法updateImageList
。接下来我们看使用updateImageList
组件的最外层组件PostEditorView
的相关代码: -
上面的代码可以看到,可以写一些自定义
hooks
来减小PostEditorView
组件的大小:useConfirmModal
封装提交内容时的弹框逻辑useAlertModal
封装图片上传失败时的弹框逻辑useUploadInput
封装选择图片相关逻辑
-
至此,上传图片页面相关组件的代码均已展示完成,建议结合思路阅读相关代码,仅提供思路,部分无关代码已删除。
展示图片页面组件
-
这部分将实现内容页的展示图片组件。
BlogImageList
组件相关代码如下: -
样式文件代码如下:
-
在展示页面,只需要将带图片id、原始宽高的图片列表传给组件即可。
-
PS:单张图使用微信朋友圈图片的显示规则,如下图所示,假设图片宽高比 X,当图片 1:1 时显示的尺寸为 Y * Y(在项目中 Y = 180,L = 4):
总结 ?
- 本文主要整理了在 React 项目实现图片上传和展示功能的相关思路和代码,涉及到使用
input[type=file]
标签来唤起系统选择图片功能,使用URL.createObjectURL()
对 File 对象进行处理实现本地图片预览,以及使用xhr
实现无刷新图片上传等知识点。
参考资料
- 前端图片上传那些事儿
- 在web应用程序中使用文件
- MDN - URL.createObjectURL()
- 微信朋友圈图片的显示规则
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论