上传文件也是咱们在实际开发中常遇到的功能,好比上传产品图片以供更好地宣传咱们的产品,上传excel文档以便于更好地展现更多的产品信息,上传zip文件以便于更好地收集一些资料信息等等。至于为什么要把上传组件封装成一个公共的、可复用的组件,在前两篇文章封装react antd的form表单组件、封装react antd的表格table组件中已经做了不少介绍,这里一样再也不赘述。html
有朋友以为这些功能组件在各类前端框架满天飞的今天都大同小异、大差不差,甚至以为Level有点Low。哈哈,其实事实也确实是这样的,咱们去二次封装这些本来已基本成熟的框架组件,逼格也确实不高,且为所欲为的空间也不大,但我以为这并不妨碍咱们的平常开发。咱们做为前端er,大部分工做都是在产品er的需求阴影下,顶着发际线愈来愈高的蒙圈脑壳,认认真真的砌墙,在胆战心惊、如履薄冰地试探“今晚会否加班”的心态中活过每个白天,已是阿弥陀佛了。若是再碰上一个好的领导,在每个阶段的开发任务结束后能再请咱们去嗨皮一番,那就真是“今天好天气,老狼请吃鸡了。” 因此,咱们能作的,只有简化开发步骤,提升组件的复用率,再完美一点就是提供可定制化的封装组件了。但这是个仁者见仁智者见智的见解,我仍是很尊重承认有独立思考能力的人!前端
话很少说,先来介绍一下封装过程当中所关注的几个点:react
上传文件势必须要咱们去关注所上传文件的大小。这个功能点,咱们放在antd所提供的upload组件的beforeUpload方法中,固然beforeUpload方法咱们也是封装在upload组件中,经过使用时传入的配置来控制文件大小。json
上传文件的格式或类型也是咱们必需要去关注的一个点,好比这个上传控件只能上传图片,那个上传控件只能上传excel文档等等,咱们可使用antd所提供的upload组件的accept来控制,固然accept也是被封装在了upload组件中。数组
antd所提供的upload组件中给咱们提供了一个action的API,官方的解释是action:上传的地址,跟form的表单提交有点相似。这种的上传方式咱们不太好控制,咱们会使用upload组件的customRequest方法来自定义上传的实现,官方对其的解释是:经过覆盖默认的上传行为,能够自定义本身的上传实现。前端框架
关于上传功能,咱们须要注意的也基本就是以上三点,至于咱们是上传到本身的服务器仍是第三方如阿里云,则不在这篇文章的介绍范围了。下面来看一下具体实现。服务器
外甥打灯笼——照舅(照旧)先放一张效果图:
效果图有点小,由于是缩略图,就不要介意这个细节了。
一、所封装的上传组件upload.jsbabel
import { createElement } from 'react' import { Upload, message } from 'antd'; const h = createElement; const SUFFIX = /.+(\.\w+)$/, BYTE = 1024, ACCEPT = { zip: 'application/zip,application/x-zip,application/x-zip-compressed', pdf: 'application/pdf', excel: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', image: 'image/jpeg,image/bmp,image/png,image/gif', }, getAccepts = accept => (Array.isArray(accept) ? accept : [accept]).map(ac => ACCEPT[ac]).join(','), beforeCheck = (config, file) => { let { accept, max = Number.MAX_VALUE } = config || {}, { size, type } = file, accepts = getAccepts(accept).split(','); //大小限制(M) if(Math.pow(BYTE, 2) * max < size){ message.info(`文件不能超过${max}M`); return false; } } const UploadComponent = props => { let { children, config } = props, { accept } = config, attrs = {}; //不能在props对象上直接添加属性,只能再定义一个attrs对象 Object.assign(attrs, { action: '', accept: getAccepts(accept), beforeUpload: file => beforeCheck(config, file), customRequest: opts => { let { file, onSuccess, onProgress, onError } = opts, { uid, name, type } = file; name = `${uid}${name.replace(SUFFIX, '$1')}`; //判断上传的文件是不是图片,若不是图片,前端可自行根据isImg来控制是否可预览文件 if(getAccepts(accept).indexOf(type) > -1) file.isImg = true // fetch('https://jsonplaceholder.typicode.com/posts', { // method: 'POST', // body: file, // }).then(r => { // let { res: { requestUrls } } = r; // requestUrls = requestUrls.length < 1 ? '' : requestUrls[0] // if (requestUrls.indexOf('?') > -1) requestUrls = requestUrls.split('?')[0] // onSuccess({ res: file, url: requestUrls }); // }) let resFile = { uid, name, url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', } onSuccess(resFile) }, }) return h(Upload, {...props, ...attrs}, children) } export default UploadComponent
代码中注释的部分模拟的就是上传到服务器,不过这个地址是从antd上抄来的,貌似不能用。antd
二、upload组件的使用方法:react-router
import React, { useState } from 'react' import Upload from './Upload' const UploadComp = () => { const config = { accept: 'image', //接受上传的文件类型:zip、pdf、excel、image,也能够是文件类型所组成的数组类型如:['image', 'pdf'],则只能够上传图片或pdf类型的文件,也能够为空,则任何类型的文件均可以上传 max: 1, //限制上传文件大小 }; const [fileList, setFileList] = useState([]) const onPreview = file => { console.log(file) } const onSuccess = res => { //useState不能向数组中push数据,只能经过这样的方式来push数据 setFileList([...fileList, res]) } return <Upload listType="picture-card" config={config} fileList={fileList} onSuccess={onSuccess} onPreview={onPreview}>{fileList.length >= 2 ? null : '上传'}</Upload> } export default UploadComp
对于封装的这个upload组件,还有一点想说的就是我只是按照antd的API作了封装,而后再把这些API传递给upload组件。若是你说我想把上传两个字给成按钮形式的能够吗?固然能够,只要antd是如何作到的,我这里就也是如何作到的。因此参照antd的官方实现方法便可。至于其余配置,若是你有实际需求,再往组件中添加就好了。
最后仍是再贴一下本次封装所用到的各个包的版本:
react: 16.8.6,
react-dom: 16.8.6,
react-router-dom: 5.0.0,
antd: 4.3.5,
@babel/core: 7.4.4,
babel-loader: 8.0.5