How to send the resized image from sharp back to user?

Viewed 2305

I'm trying to make an image resizing API through SharpJS and my code follows as per the following

require('dotenv').config();

const express = require('express');
const sharp = require('sharp');
const cors = require('cors');
const formidable = require('formidable');

const app = express();
app.use(cors());

app.use(express.json());

app.post('/file', async (req, res) => {
    const form = formidable();
    form.parse(req, (err, fields, files) => {
        if (err) {
            next(err);
            return;
        }
        const imageInput = files.image.path;
        sharp(imageInput)
            .resize(128, 128)
            .toBuffer()
            .then((data) => {
                console.log(data);
                res.end(data)
            })
            .catch((err) => console.log(err));
    });
});
app.listen(4000);

What I actually want to know is how can I return the resized image to the client without actually saving it in server disk? The data is in the form of buffer as below, How do I convert it back to a file so that users could get their images and download it automatically.

// console.log(data)
<Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 80 00 00 00 80 08 06 00 00 00 c3 3e 61 cb 00 00 00 09 70 48 59 73 00 00 0b 13 00 00 0b 13 01 ... 12835 more bytes>

My React Code

import React, {useState} from 'react'
import axios from 'axios'
function App() {
  const [state, setState] = useState({
    file: null
  })
  const fileHandler = e => {
    console.log(e.target.files[0]);
    setState({...state, file: e.target.files[0]})

  }
  const fileUploader = () => {
    const formData = new FormData()
    formData.append('image', state.file, state.file.name);
    axios.post('http://localhost:4000/file',formData).then((res) => {
      console.log(res.data)
    }).catch((err) => {
      console.error(err)
    })
  }
  return (
    <div className="App">
      <input type="file" onChange={fileHandler} />
      <button onClick={fileUploader}>Send File</button>
    </div>
  );
}
export default App;
2 Answers

Ok I asked this question and found the answer on my own.

What I discover is that in order to send the resized image back to user without saving the image on disk storage of server with using fs module, what I did is I converted that buffer into base64 in order convert it back into a blob in a browser since we cant make a blob in nodejs

sharp(imageInput)
            .resize(512, 512)
            .png()
            .toBuffer()
            .then((data) => {
                
                const base64Data = data.toString('base64');

                // const blobData = `data:${contentType};base64,${base64Data}`

                res.status(202).json({ b64Data: base64Data, contentType: contentType, extension:'png'});
                // res.send(base64Data)
            })
            .catch((err) => console.log(err));

Then I used these two libraries b64-to-blob and js-file-download to do the rest of work for me as below:

// In App.js
import fileDownload from 'js-file-download';
import b64toBlob from 'b64-to-blob'; //importing these two in react

        axios
            .post('http://localhost:4000/file', formData)
            .then((res) => {
                const data = res.data;
                // console.log(data);
                const blob = b64toBlob(data.b64Data, data.contentType);
                // console.log(blob);
                const [ fileName ] = state.file.name.split('.');
                fileDownload(blob, `${fileName}-resized.${data.extension}`);
            })
            .catch((err) => {
                console.error(err);
            });

And finally, I got what I need : A resized Image

You can use it without json() too. Sharing just in case.

router.get('/image', async (req, res, next) => {
    sharp(input)
        .resize(512, 512)
        .png()
        .toBuffer()
        .then(data => res.type('png').send(data))
})

Tested. Seems to work properly.

Related