1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-01-31

初めに

今回はNASみたいなファイルサーバーを書いていきます

モジュールのインストール

以下のコマンドを入力して今回使用するモジュールを今回のフォルダにダウンロードしてください

cmd
npm install express fs-extra cors multer archiver path

フォルダ構造

今回は同じ階層にindex.htmlとNAS.jsを作成します

コピペ

以下のコードをコピペしてください。

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:

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に書いてもらいましたが、バグったのでのせませんd。(ごめんなさい)

最後に

今回はNASみたいなものをNode.jsで書いてみました。ちなみに、クライアント側のコードはChatGPTに修正してもらいました。
それではまたお会いしましょう!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?