diff --git a/README.md b/README.md index 49a4beb..1b68305 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ### Simple image resizer -Run : +#### Run : ```bash npm i @@ -12,3 +12,35 @@ or from docker image [solehudin5699/img-resizer](https://hub.docker.com/r/solehu ```bash docker run -it --name img-resizer -p 5000:5000 -d solehudin5699/img-resizer ``` + +#### Demo : + +- Resize from uploaded image, visit : + + > Local --> http://localhost:5000/ + + > My website: https://img-resizer.escloud.my.id/ + +- Resize from image url : + + > https://img-resizer.escloud.my.id/{IMAGE-URL}{?|&}width={VALUE}&height={VALUE}&quality={90}&format={format that you want}" + +- Notes: + + - IMAGE-URL: image url that will we resize/format + - width: width that will we return (optional, max 5000) + - height: height that will we return (optional, max 5000) + - quality: quality image that will we return (optional, max 100) + - format: format image (optional, use jpeg or jpg, png, or webp) + - If IMAGE-URL have query, add "&" at the end IMAGE-URL, then add query width, height, or other that you want. + Example: + + IMAGE-URL=https://avatars.githubusercontent.com/u/65361695?v=4 + + Use: https://img-resizer.escloud.my.id/https://avatars.githubusercontent.com/u/65361695?v=4&width=100. + + If not, use "?" followed query you want, example + + IMAGE-URL=https://avatars.githubusercontent.com/u/65361695 + + Use: https://img-resizer.escloud.my.id/https://avatars.githubusercontent.com/u/65361695?width=100 diff --git a/helper/resizer.js b/helper/resizer.js index 3b5597a..f601f01 100644 --- a/helper/resizer.js +++ b/helper/resizer.js @@ -1,43 +1,53 @@ -const sharp = require('sharp'); -const fs = require('fs'); +const sharp = require("sharp"); +const fs = require("fs"); +const axios = require("axios"); const resizer = async (req, res) => { const { width, height, quality, format } = req.query; const { filename } = req.params; + + const imageUrl = req.url.startsWith("/image/resize/") + ? req.url.slice(14) + : ""; + try { + let bufferImage; + if (imageUrl) { + bufferImage = await imageUrlToBuffer(imageUrl); + } + if (width) { if (parseInt(width) > 5000) { - fs.unlinkSync('public/' + filename); return res.status(400).json({ success: false, - message: 'Error. Width must be <=5000', + message: "Error. Width must be <=5000", }); } } if (height) { if (parseInt(height) > 5000) { - fs.unlinkSync('public/' + filename); return res.status(400).json({ success: false, - message: 'Error. Height must be <=5000', + message: "Error. Height must be <=5000", }); } } if (quality) { if (parseInt(quality) > 100) { - fs.unlinkSync('public/' + filename); return res.status(400).json({ success: false, - message: 'Error. Quality must be <=100', + message: "Error. Quality must be <=100", }); } } - const image = sharp(`./public/${filename}`); + const image = sharp(bufferImage || `./public/${filename}`); let formatImage = (await image.metadata()).format; - formatImage = format || formatImage === 'svg' ? 'png' : formatImage; + + formatImage = + format === "svg" || formatImage === "svg" ? "png" : format || formatImage; const widthImage = width ? parseInt(width) : (await image.metadata()).width; const heightImage = height ? parseInt(height) @@ -51,18 +61,35 @@ const resizer = async (req, res) => { .withMetadata() .toBuffer(); - res.setHeader('content-type', 'image'); + res.setHeader("content-type", "image"); res.send(resizedImage); - - fs.unlinkSync('public/' + filename); } catch (error) { - fs.unlinkSync('public/' + filename); - res.setHeader('content-type', 'application/json'); + res.setHeader("content-type", "application/json"); res.status(500).json({ success: false, - message: 'Internal server error', + message: error?.message || "Internal server error", }); + } finally { + if (filename) { + fs.unlinkSync("public/" + filename); + } } }; +async function imageUrlToBuffer(imageUrl) { + try { + const response = await axios.get(imageUrl, { responseType: "arraybuffer" }); + + if (response.status !== 200) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const buffer = Buffer.from(response.data); + + return buffer; + } catch (error) { + throw error; + } +} + module.exports = resizer; diff --git a/index.js b/index.js index 4444733..b99cda0 100644 --- a/index.js +++ b/index.js @@ -15,10 +15,15 @@ app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(express.static("public")); +//GET UI app.get("/", async (req, res) => { res.sendFile(path.join(__dirname, "./public/index.html")); }); + +// GET/DOWNLOAD IMAGE app.get("/public/:filename", resizer); + +//UPLOAD IMAGE app.post("/", uploadMiddleware, (req, res) => { res.json({ success: true, @@ -27,6 +32,9 @@ app.post("/", uploadMiddleware, (req, res) => { }); }); +//RESIZE IMG FROM URL (query-> width,height,format,quality) +app.get("/image/resize/*", resizer); + app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); diff --git a/package-lock.json b/package-lock.json index 6c6a4dc..8947d61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,21 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "requires": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -211,6 +226,14 @@ "simple-swizzle": "^0.2.2" } }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", @@ -320,6 +343,11 @@ "gopd": "^1.0.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -452,6 +480,21 @@ "unpipe": "~1.0.0" } }, + "follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -801,6 +844,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/package.json b/package.json index 9029878..b1cc249 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^1.6.7", "body-parser": "^1.19.2", "dotenv": "^16.0.3", "express": "^4.17.3",