


Share an example tutorial on the implementation principle of the image upload component (React + Node)
This article mainly introduces the Node-based ReactPictureUpload component implementation example code, which is of great practical value. Friends in need can refer to it
Written in front
If the red flag does not fall, I vow to carry out JavaScript to the end! Today I will introduce the front-end and back-end implementation principles (React + Node) of the image upload component in my open source project Royal. It took me some time and I hope it will be helpful to you.
Front-end implementation
Following the idea of React componentization, I made the image upload an independent component (no other dependency), just import it directly.
import React, { Component } from 'react' import Upload from '../../components/FormControls/Upload/' //...... render() { return ( <p><Upload uri={'http://jafeney.com:9999/upload'} /></p> ) }
The uri parameter must be passed, which is the backend interface address for image upload. How to write the interface will be discussed below.
Rendering page
The render part of the component needs to reflect three functions:
Image selection (dialog window)
Drag-able function (drag-and-drop container)
Preview-able (preview list)
UploadButton (button)
Upload completed image address and link (information list)
Main renderFunction
render() { return ( <form action={this.state.uri} method="post" encType="multipart/form-data"> <p className="ry-upload-box"> <p className="upload-main"> <p className="upload-choose"> <input onChange={(v)=>this.handleChange(v)} type="file" size={this.state.size} name="fileSelect" accept="image/*" multiple={this.state.multiple} /> <span ref="dragBox" onDragOver={(e)=>this.handleDragHover(e)} onDragLeave={(e)=>this.handleDragHover(e)} onDrop={(e)=>this.handleDrop(e)} className="upload-drag-area"> 或者将图片拖到此处 </span> </p> <p className={this.state.files.length? "upload-preview":"upload-preview ry-hidden"}> { this._renderPreview(); // 渲染图片预览列表 } </p> </p> <p className={this.state.files.length? "upload-submit":"upload-submit ry-hidden"}> <button type="button" onClick={()=>this.handleUpload()} class="upload-submit-btn"> 确认上传图片 </button> </p> <p className="upload-info"> { this._renderUploadInfos(); // 渲染图片上传信息 } </p> </p> </form> ) }
Rendered image preview list
_renderPreview() { if (this.state.files) { return this.state.files.map((item, idx) => { return ( <p className="upload-append-list"> <p> <strong>{item.name}</strong> <a href="javascript:void(0)" rel="external nofollow" className="upload-delete" title="删除" index={idx}></a> <br/> <img src={item.thumb} className="upload-image" / alt="Share an example tutorial on the implementation principle of the image upload component (React + Node)" > </p> <span className={this.state.progress[idx]? "upload-progress": "upload-progress ry-hidden"}> {this.state.progress[idx]} </span> </p> ) }) } else { return null } }
Rendered image upload information list
_renderUploadInfos() { if (this.state.uploadHistory) { return this.state.uploadHistory.map((item, idx) => { return ( <p> <span>上传成功,图片地址是:</span> <input type="text" class="upload-url" value={item.relPath}/> <a href={item.relPath} target="_blank">查看</a> </p> ); }) } else { return null; } }
The principle of uploading images on the front end is to construct the FormData object, append() the file object to the object, and then mount it on the XMLHttpRequest object send( ) to the server.
Get the file object
To get the file object, you need to use the change event of the input input box to get the handle parameter e
onChange={(e)=>this.handleChange(e)}
Then Do the following processing:
e.preventDefault() let target = event.target let files = target.files let count = this.state.multiple ? files.length : 1 for (let i = 0; i < count; 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) })
At this time, files is an array of file objects we need, and concat it into the original files.
this.setState({files: this.state.files.concat(files)})
In this way, the next operation can get the currently selected image file through this.state.files.
Use Promise to handle asynchronous upload
File upload is asynchronous to the browser. In order to handle the subsequent multi-image upload, Promise is introduced here to handle asynchronous Operation:
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) }) }
Upload progress calculation
The advantage of using the XMLHttpRequest object to send asynchronous requests is that it can calculate the progress of request processing, which fetch does not have.
We can add event monitoring for the progress event of the xhr.upload object:
xhr.upload.addEventListener("progress", (e) => { // 处理上传进度 this.handleProgress(file, e.loaded, e.total, i); }, false)
Description: The idx parameter is the index that records the multi-image upload queue
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里显示 }
Drag-and-drop upload
Drag-and-drop files are actually very simple for HTML5, because it comes with several event listening mechanisms that can be done directly This type of processing. The following three are mainly used:
onDragOver={(e)=>this.handleDragHover(e)} onDragLeave={(e)=>this.handleDragHover(e)} onDrop={(e)=>this.handleDrop(e)}
Browser behavior when canceling drag and drop:
handleDragHover(e) { e.stopPropagation() e.preventDefault() }
Processing of dragged in files:
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 < count; 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)}) }
More Pictures are uploaded at the same time
To support multiple picture uploads, we need to set the property at the component call:
multiple = { true } // 开启多图上传 size = { 50 } // 一次最大上传数量(虽没有上限,为保证服务端正常,建议50以下)
Then we can use Promise.all() to handle asynchronous operations Queue:
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) }) }
Okay, the front-end work has been completed, and the next step is the work of Node.
Backend implementation
For convenience, the backend uses expressframework to quickly build Http services and routing. For specific projects, see my github node-image-upload. Although the logic is simple, there are still several notable points:
Cross-domain call
The backend of this project uses express, we can use res .header() sets the "allowed source" of the request to allow cross-domain calls:
res.header('Access-Control-Allow-Origin', '*');
is set to * to allow any access source, which is not very safe. It is recommended to set it to the second-level domain name you need, such as jafeney.com.
In addition to "Allow source", others include "Allow header", "Allow domain", "Allow method", "Text type", etc. Commonly used settings are as follows:
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"); }
Ajax request under ES6
Ajax request under ES6 style is different from ES5. It will be sent before the formal request is sent. A request of type OPTIONS is used as a trial. Only when the request passes, a formal request can be sent to the server.
所以服务端路由 我们还需要 处理这样一个 请求:
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模块前使用必须 先安装 imagemagick 和 graphicsmagick,Linux (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();
至于图片尺寸 使用 gm的 size() 方法来获取,处理方式如下:
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页面,它已经渗透到了互联网应用层面的方方面面,或许,叫 全栈工程师 更为贴切吧。
【相关推荐】
1. 免费js在线视频教程
3. php.cn独孤九贱(3)-JavaScript视频教程
The above is the detailed content of Share an example tutorial on the implementation principle of the image upload component (React + Node). For more information, please follow other related articles on the PHP Chinese website!

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment