Skip to content

Instantly share code, notes, and snippets.

@daveknights
Created October 2, 2022 14:22
Show Gist options
  • Save daveknights/514e1460677a325b3ae96edece03d6c5 to your computer and use it in GitHub Desktop.
Save daveknights/514e1460677a325b3ae96edece03d6c5 to your computer and use it in GitHub Desktop.
Upload images to Node server
const http = require('http')
const fs = require('fs')
const path = require('path')
const formidable = require('formidable')
const static = require('node-static')
const fileServer = new(static.Server)('./public')
const hostname = '127.0.0.1'
const port = 4000
const imgExtensions = ['.jpg', '.png', '.webp']
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'POST')
if (req.url === '/upload' && req.method.toLowerCase() === 'post') {
const form = formidable({ multiples: true })
form.parse(req, (err, fields, files) => {
let uploaded = false
let error = false
if (err) {
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' })
res.end(String(err))
return
}
const oldPath = files.file.filepath
const newPath = `${path.join(__dirname, 'public')}/${files.file.originalFilename}`
const rawData = fs.readFileSync(oldPath)
fs.writeFile(newPath, rawData, err => {
err ? error = err : uploaded = true
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ uploaded: uploaded, error: error }, null, 2))
})
})
}
if (req.url === '/images') {
const validImages = []
fs.readdir(`${__dirname}/public`, (err, files) => {
if (err)
console.log(err)
else {
for (const file of files) {
if (imgExtensions.includes(path.extname(file)))
validImages.push(file)
}
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ validImages }, null, 2))
}
})
}
if (imgExtensions.some(ext => req.url.includes(ext)))
fileServer.serve(req, res)
return
})
server.listen(port, hostname, () => console.log(`Server running at http://${hostname}:${port}/`))
let dropZone;
let imageGallery;
let galleryHeader;
const server = 'http://localhost:4000/';
const showImages = async () => {
const response = await fetch(`${server}images`);
const jsonResponse = await response.json();
imageGallery.innerHTML = '';
jsonResponse.validImages.length && galleryHeader.classList.add('show');
for (const image of jsonResponse.validImages) {
const img = document.createElement('img');
img.src = `${server}${image}`;
imageGallery.appendChild(img);
}
};
const uploadFile = async file => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${server}upload`, {
method: 'POST',
body: formData
});
const jsonResponse = await response.json();
jsonResponse.uploaded && showImages();
jsonResponse.error && console.log(jsonResponse.error);
};
const init = () => {
dropZone = document.querySelector('#drop-zone');
galleryHeader = document.querySelector('h3');
imageGallery = document.querySelector('#image-gallery');
for (const eventType of ['dragenter', 'dragover', 'dragleave', 'drop']) {
dropZone.addEventListener(eventType, e => e.preventDefault());
}
dropZone.addEventListener('dragover', () => dropZone.classList = 'drag-over');
dropZone.addEventListener('drop', e => {
if (e.dataTransfer.items) {
for (const item of [...e.dataTransfer.items]) {
if (item.kind === 'file') {
const file = item.getAsFile();
uploadFile(file);
}
}
} else {
for (const file of [...e.dataTransfer.files]) {
uploadFile(file);
}
}
dropZone.classList = '';
});
showImages();
};
window.addEventListener('load', init);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload to node server</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="container">
<h1>Drag &amp; Drop Image Uploader</h1>
<div id="drop-zone">
<h2>Drag file here to upload</h2>
</div>
<h3>Uploaded imgages</h3>
<div id="image-gallery"></div>
</main>
<script src="image-upload.js"></script>
</body>
</html>
* {
border: 0;
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
margin: 0;
}
:root {
--primary-colour: 30, 144, 255;
--highlight-colour: 7,129,140;
}
h3 {
display: none;
margin: 20px 0;
}
.show {
display: block;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 30px auto 0;
padding: 0 30px;
max-width: 800px;
}
#drop-zone {
background: rgba(var(--primary-colour), 0.1);
border: dashed 3px rgb(var(--primary-colour));
color: rgb(var(--primary-colour));
height: 300px;
margin-top: 30px;
padding: 30px;
text-align: center;
width: 100%;
}
#drop-zone.drag-over {
background: rgba(var(--highlight-colour), 0.1);
border-color: rgb(var(--highlight-colour));
color: rgb(var(--highlight-colour));
}
#image-gallery {
display: grid;
gap: 20px;
grid-template-columns: repeat(4, 1fr);
width: 100%;
}
#image-gallery img {
height: auto;
width: 100%;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment