azureazure-functionsazure-static-web-app

How do I Parse FormData containing a audio file in AZ function app in 2025


I am working on a static (nextjs) webapp on azure. I want to record my voice (like 5 to 10sec) and get a transcript of it using openai. I got the code working when I send a form with audio data with postman.

But in azure static web app (swa) there are some challenges. when I send a form with an audio file to the AZ function app, it cant extract the audio or even get a hold of the formData. It seems like when trying to get the formData, the function app just freezes and dont any show errors.

I did some research and think the issue is that the form being send needs to get parsed. I tried a couple of packages, like: formidable, multiparty and @anzp/azure-function-multipart.

When I use Formidable package to parse, for some reason I cant import any exported method and use any. I imported it as following : " import FormidableMeth,{parse} from "formidable". When I click on the "formidable" it leads to node_modules/formidable/dist/index.cjs. It doesnt seem like a package I can import from because it uses a different syntax like "require(), var".

The last one (package @anzp/azure-function-multipart) seems to be fit for AZ function app (obviously beacuse of the name :p), but it hasn't been updated in a couple of years and it uses a outdated "@azure/function@3.2.0" and I have "@azure/functions@.4.6.0". I tried using a forked version forked_azure_function_multipart which has been updated like a month ago but when I checked the package.json of the forked version I still see an older version being installed in the function app. And I see an error : "Type 'HttpRequest' is missing the following properties from type 'HttpRequest': get, parseFormBody"

I tried and use "patch package" and update the package.json, but this did not work with fixxing the issue.

I read some forums but I couldnt find a solution e.g. read stack overflow forum.

Does anyone has a idea how I could parse the formData and extract the audiofile and store it in a folder (for testing purpose before storing it in az storage) in azure function app using a static webapp made of NextJS.

example (SWA function) code frontend:

async function formAction(event: any) {

     const NewForm = new FormData();
     NewForm.append('audio', event.get('audio'), 'audio.webm');
     console.log({auidFormData: event.get('audio'), event: NewForm});

     const awaitTranscript = await fetch("/api/voiceTranscriptTrigger",{method:"POST", body: NewForm, headers: {
            "Content-Type": "multipart/form-data"
     } });
        
     if(awaitTranscript.ok){
       const response = await awaitTranscript.json(); 
       console.log({response_awaitTranscript: response});
     }
}

example code (backend) Function app (extracting the formData):

import parseMultipartFormData from "@anzp/azure-function-multipart"; 

export async function voiceTranscriptTrigger(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    
try {
     console.log(`==> before checking request : <==`);
     const { fields, files } = await parseMultipartFormData(request);
     console.log({fields: fields});
     console.log({files: files});
     return new HttpResponse(test);

} catch (e) {
      console.error("Error while trying to upload a file\n", e);
      return new HttpResponse({body: JSON.stringify({"error": "Something went wrong."})});
    }
};

app.http('voiceTranscriptTrigger', {
    methods: ['POST'],
    authLevel: 'anonymous',
    handler: voiceTranscriptTrigger
});

In advance I want to thank you for reading this and even your thought of helping me solve this issue I appreciate.

Kind Regards

=============== UPDATE ===============

I have been trying out busboy package to parse the form data containing the audiofile.

Link to my github repository containg a demo app and the AZ function app is located in the api folder.

I am getting an error while trying to use the function app (request.body.)pipeTo method according to the following article, the error I receive is "typeError [ERR_INVALID_STATE]: Invalid state: The ReadableStream is locked"

Here is the full feedback log response when I try and parse the audiofile

=========== Update V2 =============

I have updated github repository voiceTranscriptTrigger file of my demo app. With Busboy I am able to write a file within the api folder but it does not contain the full audio file, when I try and play the created file I get an error : test.webm:1 Failed to load resource: net::ERR_REQUEST_RANGE_NOT_SATISFIABLE


Solution

  • @Sampath this code below works for me in azure function v4 using busboy.

    currently it stores the file in the api folder of the function app.

    If I helped someone with this give this answer a like :D.

    async function voiceparser(request: HttpRequest, context) {
                const bodyPipe = await request.body;
                
                function readHeader(request: HttpRequest, key: string) {
                    return {[key] :Object.fromEntries(request.headers.entries())[key]}; // + `; boundary=${boundary}`
                }
    
                return new Promise((resolve, reject) => {
                
                    console.log({contentType: request.headers.values});              
                    console.log({new_header: readHeader(request, 'content-type')});
                    
                    const busboyHeader = readHeader(request, 'content-type');
                    const bb = busboy({ headers: busboyHeader });
                    
                    const saveTo = join(process.cwd(), "./test.webm",);upload-${random()}`);
                    const downloadWriteStream = createWriteStream(saveTo);
                    
                    console.log({temp_directory: saveTo});
    
                    bb.on('file', (fieldname, file, filename, encoding, mimetype) => {
                       const saveTo = join(process.cwd(), './audio.webm', filename);
                        const writeStream = createWriteStream(saveTo);
                        file.on("data", (chunk) => {
                            if(typeof chunk === 'object');
                            writeStream.write(chunk);
                        });
                  
                      });
    
                      console.log({readableStream: bodyPipe});
                      bodyPipe.pipeTo(Writable.toWeb(bb));
                })
            }
    
            voiceparser(request, context);