In diesem Artikel wird hauptsächlich der ReactBildUpload-Komponentenimplementierungsbeispielcode basierend auf Node vorgestellt, der von großem praktischem Wert ist und auf den sich Freunde beziehen können, die ihn benötigen dazu
steht vorne
Wenn die rote Fahne nicht fällt, gelobe ich, JavaScript bis zum Ende auszuführen! Heute werde ich die Front-End- und Back-End-Implementierungsprinzipien (React + Node) der Bild-Upload-Komponente in meinem Open-Source-Projekt Royal vorstellen. Es hat einige Zeit gedauert und ich hoffe, es wird Ihnen hilfreich sein.

Front-End-Implementierung
Der Idee der React-Komponentisierung folgend, habe ich den Bild-Upload zu einer unabhängigen Komponente gemacht (Nr andere Abhängigkeit), importieren Sie es einfach direkt.
1 2 3 4 5 6 7 8 9 10 | import React, { Component } from 'react'
import Upload from '../../components/FormControls/Upload/'
render() {
return (
<p><Upload uri={'http:
)
}
|
Nach dem Login kopieren
Der uri-Parameter muss übergeben werden, der die Backend--Schnittstelle -Adresse für den Bild-Upload darstellt. Wie die Schnittstelle geschrieben wird, wird weiter unten erläutert.
Rendering-Seite
Der Render-Teil der Komponente muss drei Funktionen widerspiegeln:
Bildauswahl ( Dialogfenster)
Drag-fähige Funktion (Drag-Container)
Vorschaufähig (Vorschauliste)
Hochladen -Schaltfläche (Schaltfläche)
Vollständige Bildadresse und Link (Informationsliste) hochladen
Haupt RendernFunktion
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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>
)
}
|
Nach dem Login kopieren
Vorschauliste für gerenderte Bilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | _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" />
</p>
<span className={this.state.progress[idx]?
"upload-progress" :
"upload-progress ry-hidden" }>
{this.state.progress[idx]}
</span>
</p>
)
})
} else {
return null
}
}
|
Nach dem Login kopieren
Liste mit Informationen zum Hochladen gerenderter Bilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | _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;
}
}
|
Nach dem Login kopieren
Datei-Upload
Das Prinzip des Bild-Uploads im Frontend besteht darin, das FormDataObjekt zu erstellen, das Dateiobjekt an das Objekt anzuhängen() und es dann bereitzustellen in XMLSend() für das HttpRequest-Objekt an den Server.
Dateiobjekt abrufen
Um das Dateiobjekt abzurufen, müssen Sie das Änderungsereignis des Eingabeeingabefelds verwenden, um das Handle abzurufen Parameter e
1 | onChange={(e)=>this.handleChange(e)}
|
Nach dem Login kopieren
Führen Sie dann die folgende Verarbeitung durch:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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)
})
|
Nach dem Login kopieren
Zu diesem Zeitpunkt ist files ein Array von Dateiobjekten, die wir benötigen, und verknüpfen es mit den Originaldateien.
1 | this.setState({files: this.state.files.concat(files)})
|
Nach dem Login kopieren
Auf diese Weise kann der nächste Vorgang die aktuell ausgewählte Bilddatei über this.state.files abrufen.
Verwenden Sie Promise, um den asynchronen Upload durchzuführen
Der Datei-Upload erfolgt asynchron zum Browser. Um den anschließenden Upload mehrerer Bilder zu bewältigen, wird hier Promise zur Abwicklung des asynchronen Uploads eingeführt Vorgang:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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)
})
}
|
Nach dem Login kopieren
Berechnung des Upload-Fortschritts
Der Vorteil der Verwendung des XMLHttpRequest-Objekts zum Senden asynchroner Anforderungen besteht darin, dass es den Fortschritt der Anforderungsverarbeitung berechnen kann, die abgerufen wird nicht hat.
Wir können eine Ereignisüberwachung für das Fortschrittsereignis des xhr.upload-Objekts hinzufügen:
1 2 3 4 | xhr.upload.addEventListener( "progress" , (e) => {
this.handleProgress(file, e.loaded, e.total, i);
}, false)
|
Nach dem Login kopieren
Erklärung: Der idx-Parameter ist der Index , der aufzeichnet die Multi-Image-Upload-Warteschlange
1 2 3 4 5 6 | handleProgress(file, loaded, total, idx) {
let percent = (loaded / total * 100).toFixed(2) + '%';
let _progress = this.state.progress;
_progress[idx] = percent;
this.setState({ progress: _progress })
}
|
Nach dem Login kopieren
Drag-and-Drop-Upload
Drag-and-Drop-Dateien sind für HTML5 eigentlich sehr einfach, weil es kommt mit mehreren Ereignis-Listenern Der Mechanismus kann diese Art der Verarbeitung direkt durchführen. Die folgenden drei werden hauptsächlich verwendet:
1 2 3 | onDragOver={(e)=>this.handleDragHover(e)}
onDragLeave={(e)=>this.handleDragHover(e)}
onDrop={(e)=>this.handleDrop(e)}
|
Nach dem Login kopieren
Browserverhalten beim Abbrechen von Drag & Drop:
1 2 3 4 | handleDragHover(e) {
e.stopPropagation()
e.preventDefault()
}
|
Nach dem Login kopieren
Verarbeitung von hineingezogenen Dateien:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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)})
}
|
Nach dem Login kopieren
Mehrere Bilder gleichzeitig hochladen
Mehrere Bild-Uploads unterstützen Wir müssen das Attribut am Komponentenaufrufpunkt festlegen:
1 2 | multiple = { true }
size = { 50 }
|
Nach dem Login kopieren
Dann Wir können Promise.all() verwenden, um die asynchrone Operationswarteschlange zu verwalten:
1 2 3 4 5 6 7 | 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) })
}
|
Nach dem Login kopieren
Okay, die Front-End-Arbeit ist abgeschlossen und der nächste Schritt ist die Arbeit von Node.
Backend-Implementierung
Der Einfachheit halber verwendet das Backend das ExpressFramework, um schnell HTTP-Dienste und -Routing zu erstellen. Informationen zu bestimmten Projekten finden Sie in meinem Github-Node-Image-Upload. Obwohl die Logik einfach ist, gibt es dennoch einige bemerkenswerte Punkte:
Domänenübergreifender Aufruf
Das Backend dieses Projekts verwendet Express, wir können res .header( ) legt die „erlaubte Quelle“ der Anfrage fest, um domänenübergreifende Aufrufe zuzulassen:
1 | res.header('Access-Control-Allow-Origin', '*');
|
Nach dem Login kopieren
ist auf * gesetzt, um jede Zugriffsquelle zuzulassen, die weniger sicher ist. Es wird empfohlen, den von Ihnen benötigten Domänennamen der zweiten Ebene festzulegen, z. B. jafeney.com.
Zusätzlich zu „Quelle zulassen“ gehören auch „Header zulassen“, „Domäne zulassen“, „Methode zulassen“, „Texttyp“ usw. Häufig verwendete Einstellungen sind wie folgt:
1 2 3 4 5 6 7 | 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" );
}
|
Nach dem Login kopieren
Ajax-Anfrage unter ES6
Ajax-Anfrage im ES6-Stil unterscheidet sich vom ES5-Stil und wird vor der formellen Anfrage zurückgesetzt Zuerst wird als Test eine Anfrage vom Typ OPTIONS gesendet. Erst wenn die Anfrage erfolgreich ist, kann die formelle Anfrage an den Server gesendet werden.
所以服务端路由 我们还需要 处理这样一个 请求:
1 2 3 4 5 6 7 8 | 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();
});
|
Nach dem Login kopieren
注意:该请求同样需要设置跨域。
处理上传
处理上传的图片引人了multiparty模块,用法很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | router.post('/upload', function (req, res, next) {
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);
}
});
});
|
Nach dem Login kopieren
图片处理
Node处理图片需要引入 gm 模块,它需要用 npm 来安装:
BUG说明
注意:node的图形操作gm模块前使用必须 先安装 imagemagick 和 graphicsmagick,Linux (ubuntu)上使用apt-get 安装:
1 2 | sudo apt-get install imagemagick
sudo apt-get install graphicsmagick --with-webp
|
Nach dem Login kopieren
MacOS上可以用 Homebrew 直接安装:
1 2 | brew install imagemagick
brew install graphicsmagick --with-webp
|
Nach dem Login kopieren
预设尺寸
有些时候除了原图,我们可能需要把原图等比例缩小作为预览图或者缩略图。这个异步操作还是用Promise来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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)
};
});
});
}
|
Nach dem Login kopieren
重命名图片
为了方便排序和管理图片,我们按照 “年月日 + 时间戳 + 尺寸” 来命名图片:
1 2 | var _dateSymbol = new Date ().toLocaleDateString().split('-').join('');
var _timeSymbol = new Date ().getTime().toString();
|
Nach dem Login kopieren
至于图片尺寸 使用 gm的 size() 方法来获取,处理方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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:
+ '/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!');
}
});
});
|
Nach dem Login kopieren
总结
对于大前端的工作,理解图片上传的前后端原理仅仅是浅层的。我们的口号是 “把JavaScript进行到底!”,现在无论是 ReactNative的移动端开发,还是NodeJS的后端开发,前端工程师可以做的工作早已不仅仅是局限于web页面,它已经渗透到了互联网应用层面的方方面面,或许,叫 全栈工程师 更为贴切吧。
【相关推荐】
1. 免费js在线视频教程
2. JavaScript中文参考手册
3. php.cn独孤九贱(3)-JavaScript视频教程
Das obige ist der detaillierte Inhalt vonTeilen Sie ein Beispiel-Tutorial zum Implementierungsprinzip der Bild-Upload-Komponente (React + Node).. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!