初めに
今回は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に修正してもらいました。
それではまたお会いしましょう!