简体   繁体   中英

Sending blob image from Angular to ExpressJS

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 在此处输入图像描述

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;

在此处输入图像描述

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!

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!

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.

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 .

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. 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 npm i busboy@0.0.3 . 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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM