Node.jsでファイルサーバーみたいなやつを書く

# 初めに
今回はNASみたいなファイルサーバーを書いていきます
## モジュールのインストール
以下のコマンドを入力して今回使用するモジュールを今回のフォルダにダウンロードしてください
```:cmd
npm install express fs-extra cors multer archiver path
```
# フォルダ構造
今回は同じ階層にindex.htmlとNAS.jsを作成します
# コピペ
以下のコードをコピペしてください。
```javascript:NAS.js
const express = require('express');
const multer = require('multer');
const fs = require('fs-extra');
const path = require('path');
const cors = require('cors');
const archiver = require('archiver');

const app = express();
const SAVE_DIR = path.join(__dirname, 'saves');
fs.ensureDirSync(SAVE_DIR);

app.use(cors());
app.use(express.json());
app.use(express.static(SAVE_DIR));

/* Storage settings */
const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, SAVE_DIR),
    filename: (req, file, cb) => cb(null, Date.now() + "-" + file.originalname)
});
const upload = multer({ storage });

// Get files and folders list
app.get('/check-files', async (req, res) => {
    try {
        const items = await fs.readdir(SAVE_DIR, { withFileTypes: true });
        const fileList = items.map(item => ({
            name: item.name,
            isDirectory: item.isDirectory()
        }));

        // サーバー側でファイルリストを確認
        console.log(fileList);
        
        res.json(fileList);
    } catch (err) {
        res.status(500).json({ msg: "Error reading directory", error: err.message });
    }
});

// Upload file
app.post('/save', upload.single('file'), (req, res) => {
    res.json({ msg: "Upload successfully completed.", filename: req.file.filename });
});

// Upload multiple files (folder simulation)
app.post('/save-folder', upload.array('files'), (req, res) => {
    res.json({
        msg: "Folder upload successfully completed.",
        files: req.files.map(file => file.filename)
    });
});

// Download file
app.get('/download/:filename', (req, res) => {
    const filePath = path.join(SAVE_DIR, req.params.filename);
    if (fs.existsSync(filePath)) {
        res.download(filePath);
    } else {
        res.status(404).json({ msg: "File not found" });
    }
});

// Download folder as zip
app.get('/download-folder', (req, res) => {
    const zipFilePath = path.join(__dirname, 'saves.zip');
    const output = fs.createWriteStream(zipFilePath);
    const archive = archiver('zip', { zlib: { level: 9 } });

    output.on('close', () => res.download(zipFilePath, 'saves.zip'));
    archive.on('error', err => res.status(500).json({ msg: "Error creating ZIP", error: err.message }));
    
    archive.pipe(output);
    archive.directory(SAVE_DIR, false);
    archive.finalize();
});

app.listen(3000, () => {
    console.log('NAS running on port 3000.');
});
```
index.html: 
```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload & Download</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f4f4f4;
        }

        .container1 {
            background: white;
            border: 1px solid black;
            border-radius: 5px;
            text-align: left; 
            min-height: 100px;
            margin-bottom: 10px;
            margin-top: 10px;
            overflow-x: auto;
            padding: 10px;
        }

        .container {
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
        }

        input[type="file"] {
            margin: 10px 0;
        }

        button {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 16px;
            border-radius: 5px;
            cursor: pointer;
            transition: background 0.3s;
        }

        button:hover {
            background-color: #0056b3;
        }

        .message {
            margin-top: 10px;
            font-weight: bold;
        }

        .file-link {
            display: block;
            margin-bottom: 5px;
            color: #007bff;
            text-decoration: none;
        }

        .file-link:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
<div class="container" id="main">
    <div class="container">
        <select id="Mode">
            <option value="upload">Upload</option>
            <option value="download">Download Files</option>
            <option value="check">Check Files on NAS</option>
        </select>
    </div>
    <div id="content">
        <h2>File Upload</h2>
        <input type="file" id="file">
        <br>
        <button id="btn">Upload</button>
        <p class="message" id="message"></p>
    </div>
</div>

<script>
    document.getElementById("Mode").addEventListener("change", ChangeDOM);

    function ChangeDOM() {
        const mode = document.getElementById("Mode").value;
        const content = document.getElementById("content");

        switch (mode) {
            case "upload":
                content.innerHTML = `
                    <h2>File Upload</h2>
                    <input type="file" id="file">
                    <br>
                    <button id="btn">Upload</button>
                    <p class="message" id="message"></p>
                `;
                setupUpload();
                break;
            case "download":
                content.innerHTML = `
                    <h2>Download Files</h2>
                    <div id="fileList" class="container1"></div>
                    <p class="message" id="downloadMessage"></p>
                `;
                fetchFileList();
                break;
            case "check":
                content.innerHTML = `
                    <h2>Check Files</h2>
                    Files: <div class="container1" id="files"></div>
                    <button id="check" onclick="getFiles()">Check</button>
                `;
                break;
        }
    }

    function fetchFileList() {
        fetch("http://localhost:3000/check-files")
            .then(response => response.json())
            .then(files => {
                const fileList = document.getElementById("fileList");
                fileList.innerHTML = "";

                if (Array.isArray(files)) {
                    files.forEach(file => {
                        console.log(file)
                        const fileLink = document.createElement('a');
                        fileLink.href = `http://localhost:3000/download/${file.name}`;
                        fileLink.textContent = file.name;
                        fileLink.classList.add('file-link');
                        fileList.appendChild(fileLink);
                    });
                } else {
                    fileList.innerHTML = "ファイルリストを取得できませんでした。";
                }
            })
            .catch(error => {
                console.error("Error fetching files:", error);
                const fileList = document.getElementById("fileList");
                fileList.innerHTML = "Failed to load file list.";
            });
    }

    function setupUpload() {
        document.getElementById("btn").addEventListener("click", (e) => {
            e.preventDefault();
            const fileInput = document.getElementById("file");
            const file = fileInput.files[0];
            const formData = new FormData();
            formData.append("file", file);

            fetch("http://localhost:3000/save", {
                method: "POST",
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                document.getElementById("message").textContent = "upload";
            })
            .catch(error => {
                console.error("Error uploading file:", error);
                document.getElementById("message").textContent = "File upload failed.";
            });
        });
    }

    
    setupUpload();
</script>

</body>
</html>

```

ちなみにフォルダのアップロードもChatGPTに書いてもらいましたが、バグったのでのせませんｄ。（ごめんなさい）
# 最後に
今回はNASみたいなものをNode.jsで書いてみました。ちなみに、クライアント側のコードはChatGPTに修正してもらいました。
それではまたお会いしましょう！