first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/node_modules
|
||||
package-lock.json
|
||||
@@ -0,0 +1,14 @@
|
||||
const logger = (req, res, next) => {
|
||||
const time = new Date().toUTCString();
|
||||
console.log(
|
||||
`---------------------------------------------------------------\n${time}`,
|
||||
JSON.stringify({
|
||||
method: req.method,
|
||||
path: req.path,
|
||||
...req.headers,
|
||||
})
|
||||
);
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = logger;
|
||||
@@ -0,0 +1,45 @@
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
|
||||
//Disk for save file
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, res, cb) => {
|
||||
cb(null, './public');
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const nameFormat = `${Date.now()}-${file.fieldname}${path.extname(
|
||||
file.originalname
|
||||
)}`;
|
||||
cb(null, nameFormat);
|
||||
},
|
||||
});
|
||||
|
||||
//Limit filesize
|
||||
const limits = {
|
||||
fileSize: 10_1000_1000,
|
||||
};
|
||||
|
||||
const upload = multer({
|
||||
storage,
|
||||
limits,
|
||||
});
|
||||
|
||||
const uploadMiddleware = (req, res, next) => {
|
||||
const singleUpload = upload.single('image');
|
||||
singleUpload(req, res, (err) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
success: false,
|
||||
error: err,
|
||||
});
|
||||
} else if (!req.file) {
|
||||
res.json({
|
||||
success: false,
|
||||
error: 'File not found',
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
};
|
||||
module.exports = uploadMiddleware;
|
||||
@@ -0,0 +1,65 @@
|
||||
const sharp = require('sharp');
|
||||
const fs = require('fs');
|
||||
|
||||
const resizer = async (req, res) => {
|
||||
try {
|
||||
const { width, height, quality, format } = req.query;
|
||||
const { filename } = req.params;
|
||||
|
||||
if (width) {
|
||||
if (parseInt(width) > 5000) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Error. Width must be <=5000',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (height) {
|
||||
if (parseInt(height) > 5000) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Error. Height must be <=5000',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (quality) {
|
||||
if (parseInt(quality) > 100) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Error. Quality must be <=100',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const image = sharp(`./public/${filename}`);
|
||||
let formatImage = (await image.metadata()).format;
|
||||
formatImage = format || formatImage === 'svg' ? 'png' : formatImage;
|
||||
const widthImage = width ? parseInt(width) : (await image.metadata()).width;
|
||||
const heightImage = height
|
||||
? parseInt(height)
|
||||
: (await image.metadata()).height;
|
||||
|
||||
const resizedImage = await image
|
||||
.resize(widthImage, heightImage)
|
||||
.toFormat(formatImage, {
|
||||
quality: quality ? parseInt(quality) : 100,
|
||||
})
|
||||
.withMetadata()
|
||||
.toBuffer();
|
||||
|
||||
res.setHeader('content-type', 'image');
|
||||
res.send(resizedImage);
|
||||
|
||||
fs.unlinkSync('public/' + filename);
|
||||
} catch (error) {
|
||||
res.setHeader('content-type', 'application/json');
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Internal server error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = resizer;
|
||||
@@ -0,0 +1,30 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const bodyParser = require('body-parser');
|
||||
const resizer = require('./helper/resizer');
|
||||
const uploadMiddleware = require('./helper/middleware');
|
||||
const logger = require('./helper/logger');
|
||||
|
||||
const PORT = process.env.PORT || 5000;
|
||||
const app = express();
|
||||
|
||||
app.use(logger);
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(express.static('public'));
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
res.sendFile(path.join(__dirname, './public/index.html'));
|
||||
});
|
||||
app.get('/public/:filename', resizer);
|
||||
app.post('/', uploadMiddleware, (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Success upload file',
|
||||
urlImage: `http://localhost:5000/public/${req.file.filename}`,
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "simple-image-resizer",
|
||||
"version": "1.0.0",
|
||||
"description": "resize image file",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.2",
|
||||
"express": "^4.17.3",
|
||||
"multer": "^1.4.4",
|
||||
"sharp": "^0.28.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<!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>Resize Image</title>
|
||||
<style>
|
||||
html {
|
||||
background-image: linear-gradient(
|
||||
-45deg,
|
||||
rgba(204, 83, 51, 1) 0%,
|
||||
rgba(91, 27, 186, 1) 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient 10s ease infinite;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.input-file {
|
||||
display: none;
|
||||
}
|
||||
.btn-browse {
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
border-radius: 10px;
|
||||
border: 1.5px solid rgb(255, 103, 1);
|
||||
background-color: rgba(255, 153, 0, 0.5);
|
||||
display: grid;
|
||||
place-content: center;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.btn-submit {
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
border-radius: 10px;
|
||||
border: 1.5px solid red;
|
||||
background-color: rgb(207, 69, 34);
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
.note {
|
||||
font-size: 18px;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.input-option {
|
||||
width: 220px;
|
||||
border: 1px solid rgb(207, 69, 34);
|
||||
border-radius: 10px;
|
||||
height: 30px;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
.input-option-quality {
|
||||
width: 200px;
|
||||
}
|
||||
.value-qty-img {
|
||||
width: 30px;
|
||||
text-align: right;
|
||||
color: white;
|
||||
}
|
||||
.label-option {
|
||||
width: 70px;
|
||||
color: white;
|
||||
}
|
||||
.label-option-checbox {
|
||||
width: 100px;
|
||||
color: white;
|
||||
}
|
||||
.input-wrapper {
|
||||
width: 300px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.form-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.checkbox-option {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form id="form-submit" class="container">
|
||||
<div id="btn-browse" class="btn-browse">Browse Image</div>
|
||||
<input
|
||||
id="input-file"
|
||||
class="input-file"
|
||||
type="file"
|
||||
name="image"
|
||||
id=""
|
||||
required
|
||||
/>
|
||||
<!-- form -->
|
||||
<div class="form-options">
|
||||
<div class="input-wrapper">
|
||||
<label for="quality-input" class="label-option">Quality</label>
|
||||
<input
|
||||
id="quality-input"
|
||||
class="input-option-quality"
|
||||
type="range"
|
||||
max="100"
|
||||
min="1"
|
||||
/>
|
||||
<span id="value-quality" class="value-qty-img"></span>
|
||||
</div>
|
||||
<div class="checkbox-wrapper">
|
||||
<label for="use-dimension-input" class="label-option-checbox"
|
||||
>Original Size</label
|
||||
>
|
||||
<input
|
||||
id="original-size-input"
|
||||
type="checkbox"
|
||||
class="checkbox-option"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="width-input" class="label-option">Width</label>
|
||||
<input
|
||||
id="width-input"
|
||||
class="input-option"
|
||||
type="number"
|
||||
max="5000"
|
||||
min="10"
|
||||
placeholder="Input width"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-wrapper">
|
||||
<label for="height-input" class="label-option">Height</label>
|
||||
<input
|
||||
id="height-input"
|
||||
class="input-option"
|
||||
type="number"
|
||||
max="5000"
|
||||
min="10"
|
||||
placeholder="Input height"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="btn-submit" class="btn-submit" type="submit">
|
||||
Submit & Download
|
||||
</button>
|
||||
</form>
|
||||
<a
|
||||
id="anchor-donwload"
|
||||
href=""
|
||||
download
|
||||
className="hidden"
|
||||
rel="nopoper noopener"
|
||||
target="_blank"
|
||||
></a>
|
||||
</body>
|
||||
<script>
|
||||
const formSubmit = document.getElementById('form-submit');
|
||||
const btnBrowse = document.getElementById('btn-browse');
|
||||
const inputFile = document.getElementById('input-file');
|
||||
const anchorDonwload = document.getElementById('anchor-donwload');
|
||||
const inputQuality = document.getElementById('quality-input');
|
||||
const valueQuality = document.getElementById('value-quality');
|
||||
const inputWidth = document.getElementById('width-input');
|
||||
const inputHeight = document.getElementById('height-input');
|
||||
const checkboxOriSize = document.getElementById('original-size-input');
|
||||
|
||||
if (checkboxOriSize.checked) {
|
||||
inputHeight.setAttribute('disabled', true);
|
||||
inputWidth.setAttribute('disabled', true);
|
||||
} else {
|
||||
inputHeight.removeAttribute('disabled');
|
||||
inputWidth.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
btnBrowse.addEventListener('click', (e) => {
|
||||
inputFile.click();
|
||||
});
|
||||
|
||||
inputFile.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
|
||||
const imageFile = new Image();
|
||||
imageFile.onload = function () {
|
||||
// URL.revokeObjectURL(image.src);
|
||||
btnBrowse.style.backgroundImage = `url(${URL.createObjectURL(file)})`;
|
||||
btnBrowse.style.backgroundSize = 'cover';
|
||||
btnBrowse.style.backgroundColor = 'transparent';
|
||||
btnBrowse.style.width = `70vw`;
|
||||
btnBrowse.style.height = `calc(${
|
||||
imageFile.height / imageFile.width
|
||||
}*70vw)`;
|
||||
btnBrowse.style.placeContent = 'flex-end center';
|
||||
btnBrowse.innerHTML = 'Change Image';
|
||||
};
|
||||
imageFile.src = URL.createObjectURL(file);
|
||||
});
|
||||
|
||||
inputQuality.addEventListener('change', (e) => {
|
||||
const qualityValue = e.target.value;
|
||||
valueQuality.innerHTML = qualityValue;
|
||||
});
|
||||
|
||||
checkboxOriSize.addEventListener('change', (e) => {
|
||||
const isChecked = e.target.checked;
|
||||
if (isChecked) {
|
||||
inputHeight.setAttribute('disabled', true);
|
||||
inputWidth.setAttribute('disabled', true);
|
||||
} else if (!isChecked) {
|
||||
inputHeight.removeAttribute('disabled');
|
||||
inputWidth.removeAttribute('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
formSubmit.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const heightValue = inputHeight.value;
|
||||
const widthValue = inputWidth.value;
|
||||
const isOriginalSize = checkboxOriSize.checked;
|
||||
const qualityValue = inputQuality.value;
|
||||
const imageFile = inputFile.files[0];
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('image', imageFile);
|
||||
|
||||
fetch('http://localhost:5000/', {
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
body: formData,
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((result) => {
|
||||
const params = isOriginalSize
|
||||
? `?quality=${qualityValue}`
|
||||
: `?quality=${qualityValue}&width=${widthValue}&height=${heightValue}`;
|
||||
anchorDonwload.setAttribute('href', result.urlImage + params);
|
||||
anchorDonwload.click();
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user