آپلود فایل و تصاویر با React و NodeJs


آپلود فایل باعث reload شدن صفحه می شود.آیا شما در React تازه کار هستید و از همین سبک عمومی برای آپلود فایل استفاده می کنید؟روش بهتری برای مدیریت آپلود فایل در React وجود دارد.
اگر تا اخر این آموزش با من همراه باشید، میتونید خیلی راحت نحوه آپلود عکس و فایل در react رو یاد بگیرید و در پروژه های خودتون پیاده سازی کنید.
ما از Node با React برای آپلود چندین فایل به طور همزمان استفاده خواهیم کرد.در ادامه ، اعتبارسنجی ساده ای در سمت client وجود خواهد داشت و سرانجام پس از آپلود فایل ها یک notification با استفاده از react-toastify. نمایش خواهیم داد.
مثل همیشه با استفاده از create-react-app
یک پروژه react ایجاد میکنیم و bootstrap CDN رو در فایل index.html اضافه میکنیم.
می توانید به جای ایجاد یک فرم آپلود از صفر، کدهای مورد نیاز رو از bootsnipp بگیرید.
آپلود فایل در React
برای شروع کار خیلی ساده جلو می رویم و تنها یک فایل را آپلود می کنیم.
یک تابع change handler برای زمانیکه یک فایل انتخاب شد،ایجاد میکنیم.
1 |
<input type="file" name="file" onChange={this.onChangeHandler}/> |
event.target.files
را Log بگیرید.متوجه خواهید شد که یک آرایه از فایل های ذخیره شده است.target.files[0]
در واقع فایل واقعی و مورد نیاز ما به همراه جزئیات است.
1 2 3 4 5 |
onChangeHandler=event=>{ console.log(event.target.files[0]) } |
با ذخیره کردن فایل create-react-app سریعا مرورگر را refresh خواهد کرد.
حالا ما باید file انتخاب شده را در یک state ذخیره کنیم و تنها زمانی که کاربر بر روی دکمه upload کلیک کرد،فایل انتخاب شده آپلود شود.برای اینکار در ابتدا یک state به نام selectedFile
با مقدار null تعریف می کنیم.
1 2 3 4 5 6 7 |
constructor(props) { super(props); this.state = { selectedFile: null } } |
برای ذخیره کردن فایل در state از setState استفاده میکنیم و event.target.files[0]
را به selectedFile
اختصاص می دهیم.
1 2 3 4 5 6 |
onChangeHandler=event=>{ this.setState({ selectedFile: event.target.files[0], loaded: 0, }) } |
برای اطمینان state های برنامه را با استفاده از react-devtools چک کنید.
ارسال فایل به سرور
در حال حاضر یک state داریم که فایل مورد نظر در آن ذخیره شده است.ما نیاز به یک دکمه upload و یک تابع Handler داریم تا زمانی که کاربر بر روی این دکمه کلیک کرد،فایل آپلود شود.
1 |
<button type="button" class="btn btn-success btn-block" onClick={this.onClickHandler}>Upload</button> |
در onClick event ما تابع onClickHandler
رو فراخوانی می کنیم که یک درخواست به سرور ارسال می کند.state مورد نظر به عنوان یک فایل به FormData ضمیمه می شود.
1 2 3 4 |
onClickHandler = () => { const data = new FormData() data.append('file', this.state.selectedFile) } |
در این آموزش از axios
برای درخواست های خودمون به سرور استفاده می کنیم.پس باید اون رو نصب و ایمپورت کنیم.
1 |
import axios from 'axios'; |
با استفاده از axios یک درخواست post ایجاد میکنیم که نیاز به endpoint URL و data دارد.
1 2 3 4 5 |
axios.post("http://localhost:8000/upload", data, { // receive two parameter endpoint url ,form data }) .then(res => { // then print response status console.log(res.statusText) }) |
تمامی این موارد در تابع onClickhandler
اتفاق می افتد.ما فایل را به FormData ضمیمه میکنیم،یک درخواست به سرور با استفاده از axios و POST میزنیم و نتیجه را log میگیریم.
1 2 3 4 5 6 7 8 9 10 |
onClickHandler = () => { const data = new FormData() data.append('file', this.state.selectedFile) axios.post("http://localhost:8000/upload", data, { // receive two parameter endpoint url ,form data }) .then(res => { // then print response status console.log(res.statusText) }) } |
نوع پرونده ضمیمه شده باید بررسی شود ولی در نهایت چیزی که به سرور با استفاده از axios ارسال می شود یک فایل باینری است.در ادامه ما مباحث مربوط به سرور را بیان می کنیم و بعد از آن نحوه چک کردن فایل ضمیمه شده را یاد خواهیم گرفت.
ایجاد سرور با استفاده از Node
یک فایل به نام server.js
در دایرکتوری root خود بسازید.برای ایجاد سرور با Node نیاز است نتا پکیج های express
, multer
و cors
نصب شود.
1 |
npm i express multer cors nodemon –save |
از express برای ایجاد سرور،multer
برای مدیریت کردن قایل ها و Cors برای فعال کردن درخواست های cross-origin استفاده می کنیم.Nodemon برای نظارت بر تغییرات و auto-reload استفاده می شود.این مورد اختیاری است و باید در صورت مشکل ، سرور را به صورت دستی مجدداً راه اندازی کنید.
در فایل server.js
کدهای زیر را اضافه می کنیم
1 2 3 4 |
var express = require('express'); var app = express(); var multer = require('multer') var cors = require('cors'); |
CORS middleware را فراموش نکنید
1 |
app.use(cors()) |
یک multer
instance ایجاد کنید و پوشه مورد نظر برای فایل را مشخص کنید.در کد زیر ما پوشه /public را مشخص کرده ایم.همچنین می توانید هنگام آپلود، نام جدید به پرونده اختصاص دهید. در کد زیر از نام پرونده اصلی استفاده می شود.
1 2 3 4 5 6 7 8 |
var storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, 'public') }, filename: function (req, file, cb) { cb(null, Date.now() + '-' +file.originalname ) } }) |
یک upload instance ایجاد و فایل را دریافت می کنیم.
1 |
var upload = multer({ storage: storage }).single('file') |
یک POST
route برای آپلود فایل ایجاد می کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
app.post('/upload',function(req, res) { upload(req, res, function (err) { if (err instanceof multer.MulterError) { return res.status(500).json(err) } else if (err) { return res.status(500).json(err) } return res.status(200).send(req.file) }) }); |
آپلود فایل در اینجا شروع می شود.در صورتی که در multer خطایی داشته باشیم یا error دیگر Status 500 برای کاربر برگشت داده می شود و در غیر اینصورت Status OK (200) برای client ارسال می شود.
سرور بر روی port 8000 در حال اجرا است.
1 2 3 4 5 |
app.listen(8000, function() { console.log('App running on port 8000'); }); |
برای اجرا سرور دستور nodemon server.js
را در ترمینال اجرا کنید.
حالا شما می توانید فایل خودتون رو آپلود کنید و برای اینکه اطمینان پیدا کنید که درست این عملیات انجام می شود کافی است پوشه public رو چک کنید.
آپلود چندین فایل در React
تا اینجا نحوه آپلود یک فایل رو یاد گرفتیم.در ادامه قراره که قابلیت آپلود چندین فایل رو به برنامه خودمون اضافه کنیم.
در ابتدا باید multiple
رو به input field اضافه کنیم تا چندین فایل رو قبول کنه
1 |
<input type="file" class="form-control" multiple onChange={this.onChangeHandler}/> |
بروز رسانی تابع onChangeHandler
و برداشتن index [0]. در بروزرسانی state باید فقط event.target.files رو داشته باشیم.
1 2 3 4 5 |
onChangeHandler=event=>{ this.setState({ selectedFile: event.target.files, }) } |
همچنین باید تابع onClickHandler
رو بروزرسانی کنیم و در یک حلقه فایل ها رو به FormData ضمیمه کنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
onClickHandler = () => { const data = new FormData() for(var x = 0; x<this.state.selectedFile.length; x++) { data.append('file', this.state.selectedFile[x]) } axios.post("http://localhost:8000/upload", data, { // receive two parameter endpoint url ,form data }) .then(res => { // then print response status console.log(res.statusText) }) } |
در فایل server.js
باید multer upload instance رو بروزرسانی کنیم تا آرایه ای از فایل ها رو قبول کنه.
1 |
var upload = multer({ storage: storage }).array('file') |
سرور رو Reload کنید و چندین فایل رو آپلود کنید و نتیجه رو ببینید تا مطئن شوید برنامه به خوبی کار می کند.
Validation فرم
تاکنون هیچ چیزی اشتباه نشده است اما به معنای این نیست که هرگز چنین اتفاقی نخواهد افتاد.
دلایلی که ممکن است باعث crash برنامه شود:
- تعداد بالای تصاویر برای آپلود
- آپلود یک فایل با پسوند اشتباه
- آپلود عکس با حجم خیلی زیاد
اعتبار سنجی سمت client برنامه را ایمن نمی کند اما می تواند خطاها را زود به کاربر منتقل کند و تجربه کاربر را بهبود بخشد.
#1 تعداد زیاد فایل های پیوست شده
یک تابع جدا به نام maxSelectedFile
تعریف کنید و event object رو بهش پاس بدید
برای بررسی تعدادی فایل پیوست شده از length استفاده کنید. هنگامی که تعدادی پرونده به 3 رسید ، کد زیر false برمی گردد.
1 2 3 4 5 6 7 8 9 10 11 12 |
maxSelectFile=(event)=>{ let files = event.target.files // create file object if (files.length > 3) { const msg = 'Only 3 images can be uploaded at a time' event.target.value = null // discard selected file console.log(msg) return false; } return true; } |
تابع onChangeHandler
را بروز کنید و تنها زمانی state را بروز کیند که تعداد فایل های پیوست شده کمتر از 3 عدد باشد.
1 2 3 4 5 6 7 8 9 |
onChangeHandler=event=>{ var files = event.target.files if(this.maxSelectFile(event)){ // if return true allow to setState this.setState({ selectedFile: files }) } } |
#2 فایل های پیوست شده با پسوند اشتباه
یک تابع به نام checkMimeType
ایجاد کنید و event object رو بهش پاس بدید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
checkMimeType=(event)=>{ //getting file object let files = event.target.files //define message container let err = '' // list allow mime type const types = ['image/png', 'image/jpeg', 'image/gif'] // loop access array for(var x = 0; x<files.length; x++) { // compare file type find doesn't matach if (types.every(type => files[x].type !== type)) { // create error message and assign to container err += files[x].type+' is not a supported format\n'; } }; if (err !== '') { // if message not same old that mean has error event.target.value = null // discard selected file console.log(err) return false; } return true; } |
تابع onChangeHandler
را مجدد بروزرسانی و checkMimeType
را اضاقه کنید .
1 2 3 4 5 6 7 8 9 |
onChangeHandler=event=>{ var files = event.target.files if(this.maxSelectFile(event) && this.checkMimeType(event))){ // if return true allow to setState this.setState({ selectedFile: files }) } } |
#3 فایل پیوست شده با حجم زیاد
یک تابع دیگر به نام checkFileSize
برای چک کردن حجم فایل ایجاد کنید. حداکثر حجم رو مشخص کنید و اگر حجم فایل بیشتر بود خطا برای کاربر برگردانید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
heckFileSize=(event)=>{ let files = event.target.files let size = 15000 let err = ""; for(var x = 0; x<files.length; x++) { if (files[x].size > size) { err += files[x].type+'is too large, please pick a smaller file\n'; } }; if (err !== '') { event.target.value = null console.log(err) return false } return true; } |
تابع onChangeHandler
را برای مدیریت حجم فایل ها مجدد بروز کنید
1 2 3 4 5 6 7 8 9 |
onChangeHandler=event=>{ var files = event.target.files if(this.maxSelectFile(event) && this.checkMimeType(event) && this.checkMimeType(event)){ // if return true allow to setState this.setState({ selectedFile: files }) } } |
بهبود نجربه کاربری با اضافه کردن progress bar و Toastify
به کاربر اطلاع دهید که آپلود در حال رخ دادن است بسیار بهتر از آن است که تا زمان اتمام بارگذاری در صفحه متوجه این موضوع شود.
برای بهتر کردن تجریه کاربری میتوانید از progress bar برای نشان دادن میزان آپلود و popup برای نمایش پیغام استفاده کنید.
Progress Bar
از یک state به نام loaded
با مقدار پیش فرض صفر برای بروزرسانی real-time استفاده کنید
1 2 3 4 5 6 7 |
constructor(props) { super(props); this.state = { selectedFile: null, loaded:0 } } |
loaded state در progressEvent درخواست POST تغییر می کند.
1 2 3 4 5 6 7 |
axios.post("http://localhost:8000/upload", data, { onUploadProgress: ProgressEvent => { this.setState({ loaded: (ProgressEvent.loaded / ProgressEvent.total*100), }) }, }) |
برای progress bar ما از reactstrap استفاده میکنیم بنابراین باید این کتابخانه را نصب و progress bar را از reactstrap ایمپورت کنیم.
1 |
import {Progress} from 'reactstrap'; |
اضافه کردن progress bar بعد از file picker
1 2 3 4 5 |
<div class="form-group"> <Progress max="100" color="success" value={this.state.loaded} >{Math.round(this.state.loaded,2) }%</Progress> </div> |
نمایش پیغام با toastify
react-toastify
را نصب و مشابه کد زیر ایمپورت کنید
1 2 |
import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; |
container مربوط به اون رو در یک جا قرار بدید
1 2 3 |
<div class="form-group"> <ToastContainer /> </div> |
از toast در هر جایی که مخواهید پیغام نشان دهید، استفاده کنید
اولین جای مناسب میتواند نتیجه آپلود فایل باشد
1 2 3 4 5 6 |
.then(res => { toast.success('upload success') }) .catch(err => { toast.error('upload fail') }) |
تابع checkMimeType
را برای validation بروزرسانی کنید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
checkMimeType=(event)=>{ let files = event.target.files let err = [] // create empty array const types = ['image/png', 'image/jpeg', 'image/gif'] for(var x = 0; x<files.length; x++) { if (types.every(type => files[x].type !== type)) { err[x] = files[x].type+' is not a supported format\n'; // assign message to array } }; for(var z = 0; z<err.length; z++) { // loop create toast massage event.target.value = null toast.error(err[z]) } return true; } |
شما همچنین می توانید از toast.warn(msg)
استفاده کنید
آپلود فایل در react به خوب کار می کند ، اما ما می توانیم پیشرفتهای زیادی مانند آپلود در ا cloud providers داشته باشیم ، همچنین استفاده از third-party plugins برای سایر خدمات میتوان تجربه آپلود را بهبود بخشید.
اگر این آموزش را دوست داشتید ، ممکن است بخواهید که دوره کامل آموزش ReactJs را ببینید.
[منبع]
دیدگاهتان را بنویسید