Never give up

React, Node - Image upload example 본문

WEB

React, Node - Image upload example

대기만성 개발자 2022. 2. 19. 17:00
반응형

해당 예제에서는 node로 이미지 업로드 및 이미지 리스트 및 이미지 가져오는 부분을 구현하고

 

react에서 이미지 업로드 및 가져와서 보여주기를 구현해봤습니다

 

서버쪽에 만든 api 3개

get : image/get-imglist 서버에 저장된 이미지 리스트를 가져옵니다
get : image/:url 서버에 저장된 이미지를 보여줍니다
post : image/upload 서버에 이미지를 업로드합니다

 

image/index

require('dotenv').config()
const formidable = require('formidable');
const express = require('express');
const fs = require('fs');

const router = express.Router();

const storageUrl = process.env.FILE_PATH

router.get('/get-imglist', (req, res) => {
    let imgList = []

    try {
        fs.readdirSync(storageUrl).forEach((file) => {
            imgList = [...imgList, file]
        })
    } catch (e) {
        res.send({ code: -1, data: `Get file list error : ${e}` })
        return
    }

    console.log(imgList)

    if(imgList.length === 0){
        console.log('no image')
        res.send({code: -1, data: 'There is no saved image'})
        return
    }

    res.send({ code: 1, data: imgList })
})

router.get('/:url', (req, res) => {
    const url = req.params.url

    if (fs.existsSync(`${storageUrl}${url}`)) {
        fs.readFile(`${storageUrl}${url}`, (err, data) => {
            res.end(data)
        })
    } else {
        return res.send(`No file exists : ${url}`)
    }
})

router.post('/upload', (req, res) => {
    console.log('upload')
    const form = formidable({ multiples: true });

    form.parse(req, (err, fields, files) => {
        console.log({ fields, files })
        try {
            const oldPath = files.file.filepath
            const newPath = storageUrl + files.file.newFilename + `.${fields.type}`

            console.log({ oldPath, newPath })

            fs.copyFile(oldPath, newPath, (err) => {
                if (err) throw err;
                res.write('File uploaded')
            })

            res.send({ data: { fields, files }, code: 1 });

        } catch (e) {
            res.send({ data: `Image upload failed : ${e}`, code: -1 })
        }
    });
})

module.exports = router

먼저 image/get-imglist는 formidable의 readdir을 이용해서 이미지 파일들을 리스트 형태로 보내줍니다

(이미지가 저장되지 않은경우는 간단한 메시지만 보냄)

 

image/url은 formidable의 readfile로 data를 가져와서 보내줍니다

(없는 이미지 파일인 경우 메시지를 보냄)

 

image/upload는 formidable의 parse로 payload에 담긴 이미지 파일을 가져와서

 

temp폴더에 있는 파일을 formidable의 copyFile을 이용해서 새로운 경로로 복사를 해줍니다

(oldPath에 있는 임시파일을 newPath로 복붙)

 

api.js

const response = async (url, method, data, headers) => {
  	let result
   	let code

   	try {
       	const res = await axios({ url, method, data, headers })

       	result = res.data.data
       	code = res.data.code

       	if (res.status !== 200) {
           	throw new Error(`Failed to fetch. status : ${res.status}`)
       	}
   	} catch (e) {
       	result = e
       	code = -1
   	}

   	return {
       	data: result,
       	code
   	}
}

getImageList = async () => {
    const res = await response(`${imageURL}/get-imglist`)

    return {
        code: res.code,
        data: res.data
    }
}

uploadImage = async (file, type) => {
    const formData = new FormData()
    formData.append('file', file)
    formData.append('type', type)

    const res = await response(`${imageURL}/upload`, 'POST', formData, { 'Content-Type': 'multipart/form-data;' })

    return {
        code: res.code,
        data: res.data
    }
}

getImageList에서 위에 만들은 get-imglist를 가져오는 용도로 간단한 get입니다

 

uploadImage는 upload를 할 때 사용하는 post인데, 데이터를 넣어줄때 formData 형태로 넣어주고

 

헤더에 content-type에 multipart/form-data넣어줘서 보내줍니다

 

image_upload.js

import React, { Component } from 'react'
import { API } from 'api/common'
import { Link } from 'react-router-dom'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

class ImageUploadExample extends Component {
    constructor() {
        super()

        this.state = {
            hasImage: false
        }

        this.api = new API()
        this.file = null
        this.type = null
    }

    getImageListFromServer = async () => {
        const res = await this.api.getImageList()

        console.log({ res })

        if (res.code === 1) {
            window.localStorage.setItem('imgList', res.data)
            toast('이미지 리스트 보기 버튼을 눌러주세요')
            this.setState({hasImage: true})
        } else {
            toast(res.data)
        }
    }

    getImage = (e) => {
        e.preventDefault()

        this.file = e.target.files[0]

        this.type = `${this.file.type}`.split('/')[1]
        console.log(e, this.file, this.type)
    }

    uploadImage = async () => {
        if (this.file === null) {
            alert('No image selected')
            return
        }

        if (this.type === null) {
            alert('Unknown image type')
            return
        }

        const res = await this.api.uploadImage(this.file, this.type)

        if (res.code === 1) {
            toast('이미지 업로드 완료')
        } else {
            toast(res.data)
        }
    }

    render() {
        return (
            <>
                <label>
                    파일업로드
                    <input type='file' accept='image/*' onChange={this.getImage} />

                </label>
                <button onClick={() => this.uploadImage()}>
                    업로드
                </button>
                <div style={{ margin: '10px 0 10px 0' }}>
                    <button onClick={() => this.getImageListFromServer()}>
                        이미지 리스트 가져오기
                    </button>
                </div>
                {this.state.hasImage ? (<Link to={'/img-list'}>
                    이미지 리스트 보기
                </Link>) : <></>}
                <ToastContainer />
            </>
        )
    }
}

export default ImageUploadExample

화면은 간단하게 label을 붙여준 input 그리고 업로드 버튼(uploadImage용도)

 

그리고 이미지 가져오는 버튼 그리고 이미지 리스트를 보여줄 Link로 되어있고

 

각각 필요한 메소드를 만들어줬습니다

 

getImage는 클라이언트에서 이미지를 가져오는 용도

 

uploadImage는 버튼을 눌렀을 때 위에 만든 api를 이용해서 post

 

getImageListFromServer는 이미지 리스트를 가져와서 로컬스토리지에 저장 및 알림 용도

 

image_list.js

import React, { Component } from 'react'
import ImageBlock from 'route/imageblock/imageblock'
import { Center } from 'style/styled'

const imgBaseUrl = 'http://localhost:3000/image'

class ImageList extends Component {
    constructor() {
        super()

        this.imageData = window.localStorage.getItem('imgList')

        if (this.imageData !== null) {
            this.imageList = []

            this.imageData.split(',').forEach((e) => {
                this.imageList.push(`${imgBaseUrl}/${e}`)
            })
        }
    }

    componentWillUnmount() {
        window.localStorage.removeItem('imgList')
    }

    render() {
        if (this.imageList) {
            return (
                <ImageBlock imageList={this.imageList} title='업로드 이미지' />
            )
        }

        return (
            <Center height='100vh'>
                이미지 불러오는중...
            </Center>
        )
    }
}

export default ImageList

이 부분은 이미지를 보여주는 부분으로

 

로컬 스토리지에 저장되어있는 이미지를 imageblock을 통해 보여주는 부분입니다

 

< 이미지가 4개 올라간 경우 >
< 테스트 화면 >

반응형

'WEB' 카테고리의 다른 글

Node - Nodemailer example  (2) 2022.04.26
Node - Google analytics (feat. Chart.js, Excel)  (0) 2022.04.22
React - Kakaomap example  (0) 2022.02.19
React - html, css album page clone  (0) 2022.01.08
React, Node - Rest API with sqlite3 (beginner) - 2  (0) 2022.01.08
Comments