Reactで画像アップローダーの作成

## はじめに
普段業務ではJavascript、Typescriptを用いたフロントエンドの画面ばかりを取り扱っているので、あまりバックエンドの機能に触れる機会がありませんでした。
なのでアプリの一つでも作ってみて勉強するべきだなぁと、普段から考えていました。
<br>
とはいっても作りたいものが特にないので、知り合いに聞いたところ「ファイルアップローダー」を自前で作れるようになるとフルスタックな知識が学べるようなので、今回は画像アップローダーを作成することにしました。

## 環境
```
React v18
express.js
multer
```
バックエンドはサーバーを借りて動かそうと思いましたが、無料で使えるよさげなのがなさそうなので（昔は色々あった気が...）止む無しでローカルに立ち上げます。

## 成果
![nn00.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625586/61e41c75-ee64-cc11-926d-56143825932c.png)

画像を選択し、アップロードボタンを押すことで画像がサーバーへアップロードされます。

![nn01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625586/151a0158-0ec1-1e1c-37cf-92325555fee3.png)

## 解説
特に複雑な事はしていないのですが、
### frontend
フロントエンド部分はコンポーネントライブラリの[MUI](https://mui.com/)を使用してデザインしています。
```html
    <div className="App">
      <Stack direction={"column"} sx={{ mt: 4, mb: 6 }}>
        <Box>
          <Button variant="contained" color="primary" component="label">
            <input accept="image/*" type="file" onChange={(e) => { setFile(e.target.files[0]); setFileName(e.target.files[0].name) }} style={{ display: 'none' }}/>
            画像を選択
          </Button>
          <Typography sx={{ mt: 2 }}>
            {fileName}
          </Typography>
        </Box>
        <Box sx={{ mt: 4 }}>
          <Button variant="outlined" type="button" onClick={upload} startIcon={<FileUploadIcon />} disabled={!file}>
            アップロード
          </Button>
        </Box>
      </Stack>
      <ImageList cols={3} rowHeight={200} sx={{ height: "700px" }}>
      {imgData && imgData.images.map((item) => (
        <ImageListItem key={item}>
          <img
            srcSet={`${imgUrl}${item}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
            src={`${imgUrl}${item}?w=164&h=164&fit=crop&auto=format`}
            loading="lazy"
          />
        </ImageListItem>
      ))}
      </ImageList>
      
    </div>
```
### backend
バックエンドでは、API "/upload"でファイルの送信リクエストを受け取りストレージに保存し、"/images"で保存されているファイル名一覧を取得して送り返しています。
```javascript
const express = require("express")
const cors = require("cors")
const multer = require("multer")
const fs = require('fs');
const path = require('path');

const port = 3001
const app = express()
app.use(cors())
app.use(express.json())
app.use('/img', express.static(__dirname + '/public/Files'))

const storage = multer.diskStorage({
	destination: function(req, file, cb) {
		return cb(null, "./public/Files")
	},
	filename: function(req, file, cb) {
		return cb(null, `${Date.now()}_${file.originalname}`)
	}
})

const upload = multer({storage, fileFilter: (req, file, cb) => {
	if(file.mimetype.includes("image")) {
		cb(null, true);
		return;
	}
	cb(new TypeError("Invalid File Type"));
}})

app.post("/upload", upload.single("file"), (req, res) => {
	console.log(req.body)
	console.log(req.file)
	res.status(200).send("uploaded")
})

app.get("/images", (req, res) => {
	fs.readdir("./public/Files", (err, files) => {
		if (err) {
			console.error('Error reading directory:', err);
			return res.status(500).json({ error: 'Failed to read directory' });
		}
	
		// ファイルを画像ファイルにフィルタリング
		const imageFiles = files.filter(file => {
			const ext = path.extname(file).toLowerCase();
			return ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif';
		});
	
		res.status(200).json({ images: imageFiles });
	});
})

app.listen(port, () => {
	console.log("Server is running")
})
```

Github https://github.com/gigadeli/prac_img_uploader

## おわりに
バックエンドはローカル環境かつ、expressで作成したので、当たり前ですが難易度は高くなかったです。
もっとこうDBを用意してCRUD機能も付けて...　でやりたかったのですが
多分作りきる前に燃え尽きるので、今回はやりきることを目標にしました:sunrise: