2

I'm trying to send a blob image, but I'm getting Error: Unexpected end of form using multer with Serverless Framework.

From console.log enter image description here

My understanding is I have to append it to FormData before sending it in the body, but I haven't been able to get backend to accept file without crashing

    uploadImage(imageData: File) {
        console.log('IMAGE DATA', imageData);
        let formData = new FormData();
        formData.append('file', imageData, 'file.png');
        let headers = new HttpHeaders();
        headers.append('Content-Type', 'multipart/form-data');
        headers.append('Accept', 'application/json');
        let options = { headers: headers };

        const api = environment.slsLocal + '/add-image';
        const req = new HttpRequest('PUT', api, formData, options);

        return this.http.request(req);
    }

backend


const multerMemoryStorage = multer.memoryStorage();
const multerUploadInMemory = multer({
    storage: multerMemoryStorage
});

router.put(
    '/add-image',
    multerUploadInMemory.single('file'),
    async (req, res: Response) => {
        try {
            if (!req.file || !req.file.buffer) {
                throw new Error('File or buffer not found');
            }

            console.log(`Upload Successful!`);

            res.send({
                message: 'file uploaded'
            });
        } catch (e) {
            console.error(`ERROR: ${e.message}`);

            res.status(500).send({
                message: e.message
            });
        }

        console.log(`Upload Successful!`);

        return res.status(200).json({ test: 'success' });
    }
);

app.ts

import cors from 'cors';
import express from 'express';
import routers from './routes';
const app = express();
import bodyParser from 'body-parser';

app.use(cors({ maxAge: 43200 }));
app.use(
    express.json({
        verify: (req: any, res: express.Response, buf: Buffer) => {
            req.rawBody = buf;
        }
    })
);

app.use('/appRoutes', routers.appRouter);
app.use(
    bodyParser.urlencoded({
        extended: true  // also tried extended:false
    })
);

export default app;

enter image description here

From my understanding with serverless framework I have to install npm i serverless-apigw-binary

and add

    apigwBinary:
      types: #list of mime-types
        - 'image/png'

to the custom section of the serverless template yaml file. The end goal is not to save to storage like S3, but to send the image to discord.

What am I missing? I appreciate any help!

12
  • you're missing a file processing middleware on the server, see Access file upload formData in Express Commented Jan 13, 2023 at 13:47
  • If I add multer().single('file') as middleware on request then backend crashes when sending file. I'm wondering if it's because I'm using aws serverless ExpressJS Commented Jan 13, 2023 at 17:04
  • try removing headers completely, just send formData, you also might add error handler to see what's the problem. also, try including extension to the filename, for example myImage.jpg Commented Jan 13, 2023 at 18:03
  • I tried removing headers all the way and just sending formData. then tried formData.append('name', 'myImage.png');, but same issue. I get empty {} in backend Commented Jan 13, 2023 at 18:21
  • 1
    I tried that, but I get Unexpected end of form still. I did manage to get it working though. Thanks for the suggestion though. @traynor thanks also for trying to help me figure this out. This worked for me stackoverflow.com/a/75129572/4350389 Commented Jan 16, 2023 at 1:38

4 Answers 4

0

I recently encountered something similar in a react native app. I was trying to send a local file to an api but it wasn't working. turns out you need to convert the blob file into a base64 string before sending it. What I had in my app, took in a local file path, converted that into a blob, went through a blobToBase64 function, and then I called the api with that string. That ended up working for me.

I have this code snippet to help you but this is tsx so I don't know if it'll work for angular.

function blobToBase64(blob: Blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onerror = reject;
      reader.onload = () => {
        resolve(reader.result as string);
      };
      reader.readAsDataURL(blob);
    });
  }

Hope this helps!

Sign up to request clarification or add additional context in comments.

3 Comments

I have a solution that converts it to base64 and when I send it to expressjs it says request entity too large. I looked into it and from what I learned is that sending base64 is much too inefficient size-wise. I should be able to send as a blob.
Yeah I noticed the inefficiency too as it takes multiple seconds for me to get a response from the api but it was the only thing that worked for me at the time. Anyways, I apologize my answer wasn't of any help.
I appreciate your input, but it's just not what I'm looking for.
0

You can convert your Blob to a File using

new File([blob], "filename")

and then you should be able pass that file to your existing uploadImage method.

1 Comment

With that change backend from original post above crashes and says Error: Unexpected end of form Could be something to do with using serverless framework + expressJS or multerUploadInMemory middleware, but not really sure.
0

Looks like you are passing Blob instead of File based on your console.log(). So you should convert Blob to a File before calling the server. You can change your frontend code like this:

uploadImage(imageData: File) {
  // Convert Blob to File
  const file = new File([imageData], "file_name", { type: imageData.type });

  let formData = new FormData();
  formData.append('file', file, 'file.png');

  const api = environment.slsLocal + '/add-image';
  return this.http.put(api, formData);
}

Note: For more info about converting Blob to File, you can check this StackOverflow question.

10 Comments

Maybe has something to do with using Serverless Framework with expressJS or perhaps it's the multerUploadInMemory middleware, but with that change backend crashes and says Error: Unexpected end of form. So something is still off.
Hmmm... Can you try to update your bodyParser and JSON parser like this: app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ extended: true, limit: '50mb' }));
I replaced app.use(bodyParser.urlencoded({ extended: true })); in app.ts from original post above with what you suggested, but same error
Are you still setting Content-Type headers in the frontend? If yes, can you remove all custom headers from the request in the frontend?
Headers have been removed, but that didn't change error. Here's a screenshot of error: i.postimg.cc/XNfGPrgb/image.png
|
0

The thing that got it working for me was this article. There might be something different about using Express through Serverless Framework so things like mutler and express-fileupload might not work. Or could be because it's an AWS Lambda function. I don't know this for sure though. I just know I never got it working. This article was the only thing that worked for Serverless Framework + Express.

I also had to install version 0.0.3 of busboy ie npm i [email protected]. The newer version didn't work for busboy. Newer version was saying Busboy is not a constructor

Since I'm sending the file to discord and not S3 like this article does, I had to tweak the parser.event part in this part of the article for the handler.ts

export const uploadImageRoute = async (
    event: any,
    context: Context
): Promise<ProxyResult> => {
    const parsedEvent: any = await parser(event);
    await sendImageToDiscord(parsedEvent.body.file);
    const response = {
        statusCode: 200,
        body: JSON.stringify('file sent successfully')
    };
    return response;
};

comes in as a Buffer which I was able to send as a file like this

const fs = require('fs-extra');
const cwd = process.cwd();
const { Webhook } = require('discord-webhook-node');

const webhook = new Webhook('<discord-webhook-url>');

export async function sendImageToDiscord(arrayBuffer) {
    var buffer = Buffer.from(arrayBuffer, 'base64');
    const newFileName = 'nodejs.png';
    await fs.writeFile(`./${newFileName}`, buffer, 'utf-8').then(() => {
        webhook.sendFile(`${cwd}/${newFileName}`);
    });
}
});

I hope this helps someone!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.