热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于Node的React图片上传组件完成

写在前面红旗不倒,誓把JavaScript进行到底!本日引见我的开源项目Royal里的图片上传组件的前后端完成道理(React+Node),花了一些时刻,愿望对你有所协助。前端完成
写在前面

红旗不倒,誓把Javascript进行到底!本日引见我的开源项目 Royal 里的图片上传组件的前后端完成道理(React + Node),花了一些时刻,愿望对你有所协助。

《基于Node的React图片上传组件完成》

前端完成

遵照React 组件化的头脑,我把图片上传做成了一个自力的组件(没有其他依靠),直接import即可。

import React, { Component } from 'react'
import Upload from '../../components/FormControls/Upload/'
//......
render() {
return (


)
}

uri 参数是必需传的,是图片上传的后端接口地点,接口怎样写下面会讲到。

衬着页面

组件render部份须要表现三个功用:

  • 图片拔取(dialog窗口)

  • 可拖拽功用(拖拽容器)

  • 可预览(预览列表)

  • 上传按钮 (button)

  • 上传完成图片地点和链接 (信息列表)

render函数

render() {
return (




OnChange={(v)=>this.handleChange(v)}
type="file"
size={this.state.size}
name="fileSelect"
accept="image/*"
multiple={this.state.multiple} />
OnDragOver={(e)=>this.handleDragHover(e)}
OnDragLeave={(e)=>this.handleDragHover(e)}
OnDrop={(e)=>this.handleDrop(e)}
className="upload-drag-area">
也许将图片拖到此处


"upload-preview":"upload-preview ry-hidden"}>
{ this._renderPreview(); // 衬着图片预览列表 }


"upload-submit":"upload-submit ry-hidden"}>



{ this._renderUploadInfos(); // 衬着图片上传信息 }



)
}

衬着图片预览列表

_renderPreview() {
if (this.state.files) {
return this.state.files.map((item, idx) => {
return (



{item.name}
className="upload-delete"
title="删除" index={idx}>





"upload-progress":
"upload-progress ry-hidden"}>
{this.state.progress[idx]}


)
})
} else {
return null
}
}

衬着图片上传信息列表

_renderUploadInfos() {
if (this.state.uploadHistory) {
return this.state.uploadHistory.map((item, idx) => {
return (


上传胜利,图片地点是:

检察


);
})
} else {
return null;
}
}

文件上传

前端要完成图片上传的道理就是经由过程构建FormData对象,把文件对象append()到该对象,然后挂载在XMLHttpRequest对象上 send() 到效劳端。

猎取文件对象

猎取文件对象须要借助 input 输入框的 change 事宜来猎取 句柄参数 e

OnChange={(e)=>this.handleChange(e)}

然后做以下处置惩罚:

e.preventDefault()
let target = event.target
let files = target.files
let count = this.state.multiple ? files.length : 1
for (let i = 0; i files[i].thumb = URL.createObjectURL(files[i])
}
// 转换为真正的数组
files = Array.prototype.slice.call(files, 0)
// 过滤非图片范例的文件
files = files.filter(function (file) {
return /image/i.test(file.type)
})

这时刻 files 就是 我们须要的文件对象构成的数组,把它 concat 到原有的 files里。

this.setState({files: this.state.files.concat(files)})

云云,接下来的操纵 就能够 经由过程 this.state.files 取到当前已选中的 图片文件。

应用Promise处置惩罚异步上传

文件上传关于浏览器来讲是异步的,为了处置惩罚 接下来的多图上传,这里引入了 Promise来处置惩罚异步操纵:

upload(file, idx) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
if (xhr.upload) {
// 上传中
xhr.upload.addEventListener("progress", (e) => {
// 处置惩罚上传进度
this.handleProgress(file, e.loaded, e.total, idx);
}, false)
// 文件上传胜利或是失利
xhr.Onreadystatechange= (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 上传胜利操纵
this.handleSuccess(file, xhr.responseText)
// 把该文件从上传行列中删除
this.handleDeleteFile(file)
resolve(xhr.responseText);
} else {
// 上传失足处置惩罚
this.handleFailure(file, xhr.responseText)
reject(xhr.responseText);
}
}
}
// 最先上传
xhr.open("POST", this.state.uri, true)
let form = new FormData()
form.append("filedata", file)
xhr.send(form)
})
}

上传进度盘算

应用XMLHttpRequest对象发异步要求的优点是能够 盘算要求处置惩罚的进度,这是fetch所不具备的。
我们能够为 xhr.upload 对象的 progress 事宜增加事宜监听:

xhr.upload.addEventListener("progress", (e) => {
// 处置惩罚上传进度
this.handleProgress(file, e.loaded, e.total, i);
}, false)

申明:idx参数是记载多图上传行列的索引

handleProgress(file, loaded, total, idx) {
let percent = (loaded / total * 100).toFixed(2) + '%';
let _progress = this.state.progress;
_progress[idx] = percent;
this.setState({ progress: _progress }) // 反应到DOM里显现
}

拖拽上传

拖拽文件关于HTML5来讲实在异常简朴,由于它自带的几个事宜监听机制能够直接做这类处置惩罚。重要用到的是下面三个:

OnDragOver={(e)=>this.handleDragHover(e)}
OnDragLeave={(e)=>this.handleDragHover(e)}
OnDrop={(e)=>this.handleDrop(e)}

作废拖拽时的浏览器行动:

handleDragHover(e) {
e.stopPropagation()
e.preventDefault()
}

处置惩罚拖拽进来的文件:

handleDrop(e) {
this.setState({progress:[]})
this.handleDragHover(e)
// 猎取文件列表对象
let files = e.target.files || e.dataTransfer.files
let count = this.state.multiple ? files.length : 1
for (let i = 0; i files[i].thumb = URL.createObjectURL(files[i])
}
// 转换为真正的数组
files = Array.prototype.slice.call(files, 0)
// 过滤非图片范例的文件
files = files.filter(function (file) {
return /image/i.test(file.type)
})
this.setState({files: this.state.files.concat(files)})
}

多图同时上传

支撑多图上传我们须要在组件挪用途设置属性:

multiple = { true } // 开启多图上传
size = { 50 } // 一次最大上传数目(虽没有上限,为保证效劳端一般,发起50以下)

然后我们能够运用 Promise.all() 处置惩罚异步操纵行列:

handleUpload() {
let _promises = this.state.files.map((file, idx) => this.upload(file, idx))
Promise.all(_promises).then( (res) => {
// 悉数上传完成
this.handleComplete()
}).catch( (err) => { console.log(err) })
}

好了,前端事情已完成,接下来就是Node的事情了。

后端完成

为了轻易,后端采纳的是express框架来疾速搭建Http效劳和路由。详细项目见我的github node-image-upload。逻辑虽然简朴,但照样有几个可圈可点的处所:

跨域挪用

本项目后端采纳的是express,我们能够经由过程 res.header() 设置 要求的 “许可源” 来许可跨域挪用:

res.header('Access-Control-Allow-Origin', '*');

设置为 * 申明许可任何 接见源,不太平安。发起设置成 你须要的 二级域名,如 jafeney.com

除了 “许可源” ,其他另有 “许可头” 、”许可域”、 “许可要领”、”文本范例” 等。经常使用的设置以下:

function allowCross(res) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
}

ES6下的Ajax要求

ES6作风下的Ajax要乞降ES5不太一样,在正式的要求发出之前都邑先发一个 范例为 OPTIONS的要求 作为探索,只有当该要求经由过程今后,正式的要求才发向效劳端。

所以效劳端路由 我们还须要 处置惩罚如许一个 要求:

router.options('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});

注重:该要求一样须要设置跨域。

处置惩罚上传

处置惩罚上传的图片惹人了multiparty模块,用法很简朴:

/*运用multiparty处置惩罚上传的图片*/
router.post('/upload', function(req, res, next) {
// 天生multiparty对象,并设置上传目的途径
var form = new multiparty.Form({uploadDir: './public/file/'});
// 上传完成后处置惩罚
form.parse(req, function(err, fields, files) {
var filesTmp = JSON.stringify(files, null, 2);
var relPath = '';
if (err) {
// 保留失利
console.log('Parse error: ' + err);
} else {
// 图片保留胜利!
console.log('Parse Files: ' + filesTmp);
// 图片处置惩罚
processImg(files);
}
});
});

图片处置惩罚

Node处置惩罚图片须要引入 gm 模块,它须要用 npm 来装置:

npm install gm --save

BUG申明

注重:node的图形操纵gm模块前运用必需 先装置 imagemagickgraphicsmagickLinux (ubuntu)上运用apt-get 装置:

sudo apt-get install imagemagick
sudo apt-get install graphicsmagick --with-webp // 支撑webp花样的图片

MacOS上能够用 Homebrew 直接装置:

brew install imagemagick
brew install graphicsmagick --with-webp // 支撑webp花样的图片

预设尺寸

有些时刻除了原图,我们能够须要把原图等比例减少作为预览图也许缩略图。这个异步操纵照样用Promise来完成:

function reSizeImage(paths, dstPath, size) {
return new Promise(function(resolve, reject) {
gm(dstPath)
.noProfile()
.resizeExact(size)
.write('.' + paths[1] + '@' + size + '00.' + paths[2], function (err) {
if (!err) {
console.log('resize as ' + size + ' ok!')
resolve()
}
else {
reject(err)
};
});
});
}

重定名图片

为了轻易排序和治理图片,我们根据 “年月日 + 时刻戳 + 尺寸” 来定名图片:

var _dateSymbol = new Date().toLocaleDateString().split('-').join('');
var _timeSymbol = new Date().getTime().toString();

至于图片尺寸 运用 gmsize() 要领来猎取,处置惩罚方式以下:

gm(uploadedPath).size(function(err, size) {
var dstPath = './public/file/' + _dateSymbol + _timeSymbol
+ '_' + size.width + 'x' + size.height + '.'
+ _img.originalFilename.split('.')[1];
var _port = process.env.PORT || '9999';
relPath = 'http://' + req.hostname + ( _port!==80 ? ':' + _port : '' )
+ '/file/' + _dateSymbol + _timeSymbol + '_' + size.width + 'x'
+ size.height + '.' + _img.originalFilename.split('.')[1];
// 重定名
fs.rename(uploadedPath, dstPath, function(err) {
if (err) {
reject(err)
} else {
console.log('rename ok!');
}
});
});
总结

关于大前端的事情,明白图片上传的前后端道理仅仅是浅层的。我们的标语是 “把Javascript进行到底!”,如今无论是 ReactNative的挪动端开辟,照样NodeJS的后端开辟,前端工程师能够做的事情早已不仅仅是局限于web页面,它已渗入到了互联网应用层面的各个方面,也许,叫 全栈工程师 更加贴切吧。

固然,全栈 两个字的重量很重,不积跬步,无以致千里,功力低下的我还须要不停修炼和实践!

参考

张鑫旭 《基于HTML5的可预览多图片Ajax上传》

@迎接关注我的 github 和 个人博客 -Jafeney


推荐阅读
  • node.jsrequire和ES6导入导出的区别原 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 深入理解CSS中的margin属性及其应用场景
    本文主要介绍了CSS中的margin属性及其应用场景,包括垂直外边距合并、padding的使用时机、行内替换元素与费替换元素的区别、margin的基线、盒子的物理大小、显示大小、逻辑大小等知识点。通过深入理解这些概念,读者可以更好地掌握margin的用法和原理。同时,文中提供了一些相关的文档和规范供读者参考。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
author-avatar
喜怒哀乐168_572
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有