<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Fiyin Akinsiku - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Fiyin Akinsiku - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 26 May 2026 16:22:49 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/FiyinAkinsiku/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Upload Files to Amazon S3 with Node.js ]]>
                </title>
                <description>
                    <![CDATA[ File upload is a common feature in a lot of modern applications. These platforms accept different file formats, including jpeg, png, gif, pdf, txt, zip, and mp3.  Some applications will also restrict uploads to a specific file type. For example, when... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-upload-files-to-aws-s3-with-node/</link>
                <guid isPermaLink="false">66ba2d45b55d1a5c141ccc51</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fiyin Akinsiku ]]>
                </dc:creator>
                <pubDate>Tue, 25 Apr 2023 16:31:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/pexels-cottonbro-studio-3584994.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>File upload is a common feature in a lot of modern applications. These platforms accept different file formats, including jpeg, png, gif, pdf, txt, zip, and mp3. </p>
<p>Some applications will also restrict uploads to a specific file type. For example, when uploading your resume on LinkedIn, you will see a subtext specifying DOC, DOCX, and PDF.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screen-Shot-2023-03-27-at-10.08.32-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Upload resume page on LinkedIn</em></p>
<p>Some other file upload use cases are:</p>
<ul>
<li>Adding a profile picture on Instagram.</li>
<li>Adding a product image to a Shopify store.</li>
<li>Adding a screenshot to a freeCodeCamp tutorial.</li>
<li>Selling digital products such as PDF files on Selar.</li>
</ul>
<p>Where do uploaded files go? It is advisable to save your files using cloud storage service providers. They allow users to access the file from anywhere. They also provide secure storage for the files. For example, Amazon Simple Storage Service (S3) has access policy settings which allow you to determine who has access to your S3 bucket.</p>
<p>You can read more about Amazon S3 security and access management on the <a target="_blank" href="https://aws.amazon.com/s3/security/">official website</a>.</p>
<p>In this tutorial, I will show you how to upload files to an S3 bucket with a Node.js application.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow this tutorial, you should understand the following:</p>
<ul>
<li>Basic HTML.</li>
<li>The Node.js server.</li>
<li>The Express.js framework.</li>
</ul>
<p>You should also have an AWS account with an active S3 bucket. There are clear steps for <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/setting-up-s3.html#sign-up-for-aws">setting up your AWS account</a> and <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html">creating an S3 bucket</a> in the AWS documentation.</p>
<p>Now, let's get started.</p>
<h2 id="heading-how-to-create-the-nodejs-server">How to Create the Node.js Server</h2>
<p>This application will use a couple of packages:</p>
<ul>
<li><code>express</code>: This is a <a target="_blank" href="https://www.freecodecamp.org/news/p/994c216e-e3df-47b3-8e00-2a899a78aa45/Node.js">Node.</a>js framework for APIs.</li>
<li><code>dotenv</code>: This is to access environment variables in the <code>.env</code> file.</li>
<li><code>formidable</code>: This is a data parser that supports file uploads.</li>
<li><code>@aws-sdk/lib-storage</code>: This is an AWS SDK library for uploading large files.</li>
<li><code>@aws-sdk/client-s3</code>: This is an AWS SDK S3 Client for Node.js.</li>
</ul>
<p>Install them by running this command:</p>
<pre><code class="lang-js">npm install express dotenv formidable @aws-sdk/lib-storage @aws-sdk/client-s3
</code></pre>
<p>Next, create an <code>index.js</code> file to set up the server. Since this application is for tutorial purposes, I will set up the HTML form in the same file. You can go with a different setup.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

app.set(<span class="hljs-string">'json spaces'</span>, <span class="hljs-number">5</span>); <span class="hljs-comment">// to pretify json response</span>

<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">3000</span>;

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">`
    &lt;h2&gt;File Upload With &lt;code&gt;"Node.js"&lt;/code&gt;&lt;/h2&gt;
    &lt;form action="/api/upload" enctype="multipart/form-data" method="post"&gt;
      &lt;div&gt;Select a file: 
        &lt;input type="file" name="file" multiple="multiple" /&gt;
      &lt;/div&gt;
      &lt;input type="submit" value="Upload" /&gt;
    &lt;/form&gt;

  `</span>);
});

app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${PORT}</span>.`</span>)
})
</code></pre>
<p>Start the server with the  <code>node index.js</code> command.</p>
<p>To confirm that everything works as expected, check for the message <code>Server running on port 3000.</code> on your terminal.</p>
<p>With the server up and running, open this link <a target="_blank" href="http://127.0.0.1:3000">http://127.0.0.1:3000/</a> (replace <code>3000</code> with your port number) on your browser. It should render a form on the webpage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screen-Shot-2023-03-24-at-12.29.33-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Form page for the file upload</em></p>
<h2 id="heading-how-to-configure-the-file-parser">How to Configure the File Parser</h2>
<p>We will serve the files from the client to the S3 bucket using the Node.js <code>formidable</code> package.</p>
<p>The <code>formidable</code> module accepts an <code>options</code> object that contains a range of file properties. These properties have default values which you can overwrite to configure the parser according to your needs. Some of the keys are:</p>
<ul>
<li><code>allowEmptyFiles</code>: This is assigned a boolean value. It determines if empty files should be allowed and its value is <code>true</code> by default.</li>
<li><code>minFileSize</code>: This key accepts a number value representing the smallest file size allowed. The default value is 1 byte.</li>
<li><code>maxFileSize</code>: This also accepts a number value. It represents the largest file size allowed. The default value is 200 megabytes (MB).</li>
</ul>
<p>I want my app to accept files smaller than or equal to 100 MB in size, so I must set it as the <code>maxFileSize</code> option in the options object.</p>
<p>Open a <code>fileparser.js</code> file and add the code below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formidable = <span class="hljs-built_in">require</span>(<span class="hljs-string">'formidable'</span>);

<span class="hljs-keyword">const</span> parsefile = <span class="hljs-keyword">async</span> (req) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">let</span> options = {
            <span class="hljs-attr">maxFileSize</span>: <span class="hljs-number">100</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">//100 MBs converted to bytes,</span>
            <span class="hljs-attr">allowEmptyFiles</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-keyword">const</span> form = formidable(options);

        form.parse(req, <span class="hljs-function">(<span class="hljs-params">err, fields, files</span>) =&gt;</span> {});
    })
}

<span class="hljs-built_in">module</span>.exports = parsefile;
</code></pre>
<p>This service returns a promise that resolves with the upload details if successful. Otherwise, it rejects with the relevant error details. It generates a <code>form</code> instance that does not accept empty uploads and will only parse 100 MB or less. It also calls the <code>form.parse()</code> method to process the incoming request.</p>
<p>The parse method accepts two arguments – the request payload and a callback function. You can access fields, files, and any <code>parse()</code> error (as <code>err</code>) details here.</p>
<blockquote>
<p>You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any <code>'field'</code> / <code>'file'</code> events processing which would occur otherwise, making you fully responsible for handling the processing. (<a target="_blank" href="https://www.npmjs.com/package/formidable">Formidable</a>)</p>
</blockquote>
<p>We will not be overwriting this part since we need the form instance to stream the file to the S3 bucket. We will explore this later in this tutorial.</p>
<h3 id="heading-how-to-handle-form-events">How to Handle Form Events</h3>
<p>If you are unfamiliar with events in programming, take a look at this definition before continuing: </p>
<blockquote>
<p>Events are things that happen in the system you are programming, which the system tells you about so your code can react to them. (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events">Source: MDN</a>)</p>
</blockquote>
<p>The <code>form</code> instance emits different events while processing a file, including:</p>
<ul>
<li>An <code>error</code> event for errors in the parsing process.</li>
<li>A <code>file</code> event when it receives a file/field pair.</li>
<li>A <code>progress</code> event after parsing each chunk of data.</li>
</ul>
<p>You can specify how you want to handle these events using the <code>form.on()</code> method.</p>
<p>The <code>on()</code> method accepts an event name and a listener function, which is triggered whenever the form emits the event. The request will sometimes timeout, like in an <code>error</code> event, if the <code>event</code> is not handled. You can control what happens when the program emits a particular event in the listener function.</p>
<p>Update the <code>fileparser.js</code> file with the <code>on()</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formidable = <span class="hljs-built_in">require</span>(<span class="hljs-string">'formidable'</span>);

<span class="hljs-keyword">const</span> parsefile = <span class="hljs-keyword">async</span> (req) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">let</span> options = {
            <span class="hljs-attr">maxFileSize</span>: <span class="hljs-number">100</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">//100 MBs converted to bytes,</span>
            <span class="hljs-attr">allowEmptyFiles</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-keyword">const</span> form = formidable(options);

        form.parse(req, <span class="hljs-function">(<span class="hljs-params">err, fields, files</span>) =&gt;</span> {});

        form.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
            reject(error.message)
        })

        form.on(<span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (data.name === <span class="hljs-string">"successUpload"</span>) {
                resolve(data.value);
            }
        })


    })
}

<span class="hljs-built_in">module</span>.exports = parsefile;
</code></pre>
<p>The <code>data</code> listener will return a successful response when the S3 upload is successful S3. However, the <code>error</code> listener will listen for error events and send the error response to the client. </p>
<p>For example, I get this error message when I try to upload an empty file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screen-Shot-2023-04-24-at-10.43.29-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error response for an empty file upload</em></p>
<p>Another event you will listen for is the <code>fileBegin</code> event.</p>
<p>Disclaimer: I came across this <a target="_blank" href="https://github.com/node-formidable/formidable/issues/227#issuecomment-289223225">solution</a> on GitHub. I tweaked it to fit my use case, and it worked perfectly.</p>
<blockquote>
<p>It is emitted whenever a new file is detected in the upload stream. Use this event if you want to stream the file to somewhere else while buffering the upload on the file system. (<a target="_blank" href="https://www.npmjs.com/package/formidable">Formidable</a>)</p>
</blockquote>
<h3 id="heading-how-to-create-the-file-stream">How to Create the File Stream</h3>
<p>A benefit of streaming is that we do not need to wait to receive the entire file before processing. Every chunk of data is processed as it is received.</p>
<p>Import the <code>Transform</code> class from the Node.js <code>stream</code> module into the <code>fileparser.js</code> file. This class will create a transform stream which will pass as the body of the uploaded object.</p>
<blockquote>
<p>The transform stream is a type of duplex stream that reads data, transforms the data, and then writes the transformed data in a specified format. (<a target="_blank" href="https://blog.logrocket.com/working-node-js-streams/#transform-streams">LogRocket Blog</a>)</p>
</blockquote>
<p>Next, call the <code>form.on</code> method with a <code>fileBegin</code> event name and the listener function.</p>
<p>What does the listener function here do? </p>
<p>The function has two parameters – <code>formName</code> and <code>file</code>. They represent the name of the form that contains the file and the file object containing details of the file, respectively. The focus in this section is the <code>file</code> object.</p>
<p><code>file</code> is an instance of the Formidable <code>PersistentFile</code> <a target="_blank" href="https://github.com/node-formidable/formidable/blob/master/src/PersistentFile.js">class</a> created from the Node.js <code>EventEmitter</code> class. The <code>PersistentFile</code> class has some methods, including <code>open()</code> and <code>end()</code>. You can overwrite them to determine what happens when any is triggered.</p>
<p>Create an asynchronous function to handle the stream upload and assign it to the <code>file.open</code> method.</p>
<p>Update the <code>fileParser.js</code> file as shown:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formidable = <span class="hljs-built_in">require</span>(<span class="hljs-string">'formidable'</span>);
<span class="hljs-keyword">const</span> Transform = <span class="hljs-built_in">require</span>(<span class="hljs-string">'stream'</span>).Transform;

<span class="hljs-keyword">const</span> parsefile = <span class="hljs-keyword">async</span> (req) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">let</span> options = {
            <span class="hljs-attr">maxFileSize</span>: <span class="hljs-number">100</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">//100 MBs converted to bytes,</span>
            <span class="hljs-attr">allowEmptyFiles</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-keyword">const</span> form = formidable(options);

        form.parse(req, <span class="hljs-function">(<span class="hljs-params">err, fields, files</span>) =&gt;</span> {});

        form.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
            reject(error.message)
        })

        form.on(<span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (data.name === <span class="hljs-string">"successUpload"</span>) {
                resolve(data.value)
            }
        })

        form.on(<span class="hljs-string">'fileBegin'</span>, <span class="hljs-function">(<span class="hljs-params">formName, file</span>) =&gt;</span> {

            file.open = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
                <span class="hljs-built_in">this</span>._writeStream = <span class="hljs-keyword">new</span> Transform({
                    transform(chunk, encoding, callback) {
                        callback(<span class="hljs-literal">null</span>, chunk)
                    }
                })

                <span class="hljs-built_in">this</span>._writeStream.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
                    form.emit(<span class="hljs-string">'error'</span>, e)
                });
            }

        })


    })
}

<span class="hljs-built_in">module</span>.exports = parsefile;
</code></pre>
<p>This function creates an instance of the <code>Transform</code> class that processes the data chunks with an internal <code>transform</code> method. The resulting stream is then set as the write stream for the <code>file</code> object using <code>this._writeStream</code>.</p>
<p>Next, we add the <code>on</code> listener to <code>this._writeStream</code>  to handle any errors in the streaming process.</p>
<h3 id="heading-how-to-upload-to-the-s3-bucket">How to Upload to the S3 Bucket</h3>
<p>This is where the file storage happens.</p>
<p>Import the <code>Upload</code> module of the <code>@aws-sdk/lib-storage</code> package in the <code>fileparser.js</code> file. The module allows us to upload the file in parts. We will also import the <code>S3Client</code> from <code>@aws-sdk/client-s3</code>. </p>
<pre><code><span class="hljs-keyword">const</span> { Upload } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/lib-storage"</span>);
<span class="hljs-keyword">const</span> { S3Client, S3 } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/client-s3"</span>);
</code></pre><p>You will need your AWS credentials to configure the upload.</p>
<ul>
<li>Follow the <a target="_blank" href="https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.html">instructions</a> on the AWS website to retrieve the access keys.</li>
<li>Check the <a target="_blank" href="https://s3.console.aws.amazon.com/s3/buckets?">S3 console</a> to confirm your bucket name and region.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screen-Shot-2023-04-23-at-3.50.38-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Add them to your <code>.env</code> file and assign them to the <code>fileparser.js</code> file.</li>
</ul>
<pre><code>PORT=<span class="hljs-number">3000</span>
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_REGION=eu-central<span class="hljs-number">-1</span>
S3_BUCKET=prac-s3
</code></pre><p>Next, you will create a new instance of the <code>Upload</code> module imported from <code>@aws-sdk/lib-storage</code> within the <code>file.open</code> method. We will configure that instance with some options, including:</p>
<ul>
<li><code>client</code>: This is the destination of the file. Since we are uploading to an S3 bucket, we will use the <code>S3Client</code> provided by AWS. Create a new instance of the <code>S3Client</code> and add your AWS credentials – AWS secret access key and AWS access key ID – to configure it. You also need to specify the bucket's region in the client.</li>
<li><code>params</code>: This object contains the name of the S3 bucket (Bucket), the <code>Key</code> (that is, the file name), the access control list (ACL) that defines access to the data, and the <code>Body</code> (that is, the generated transform stream).</li>
<li><code>queueSize</code>: This defines the number of parts to be processed simultaneously. The default is 4.</li>
<li><code>partSize</code>: This defines the size of each part that is processed. The smallest size possible is 5MB.</li>
</ul>
<p>Here is the updated <code>fileparser.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { Upload } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/lib-storage"</span>);
<span class="hljs-keyword">const</span> { S3Client } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/client-s3"</span>);
<span class="hljs-keyword">const</span> Transform = <span class="hljs-built_in">require</span>(<span class="hljs-string">'stream'</span>).Transform;

<span class="hljs-keyword">const</span> accessKeyId = process.env.AWS_ACCESS_KEY_ID;
<span class="hljs-keyword">const</span> secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
<span class="hljs-keyword">const</span> region = process.env.S3_REGION;
<span class="hljs-keyword">const</span> Bucket = process.env.S3_BUCKET;

<span class="hljs-keyword">const</span> parsefile = <span class="hljs-keyword">async</span> (req) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">let</span> options = {
            <span class="hljs-attr">maxFileSize</span>: <span class="hljs-number">100</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">//100 MBs converted to bytes,</span>
            <span class="hljs-attr">allowEmptyFiles</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-keyword">const</span> form = formidable(options);

        form.parse(req, <span class="hljs-function">(<span class="hljs-params">err, fields, files</span>) =&gt;</span> {});

        form.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
            reject(error.message)
        })

        form.on(<span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (data.name === <span class="hljs-string">"successUpload"</span>) {
                resolve(data.value);
            }
        })

        form.on(<span class="hljs-string">'fileBegin'</span>, <span class="hljs-function">(<span class="hljs-params">formName, file</span>) =&gt;</span> {

            file.open = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
                <span class="hljs-built_in">this</span>._writeStream = <span class="hljs-keyword">new</span> Transform({
                    transform(chunk, encoding, callback) {
                        callback(<span class="hljs-literal">null</span>, chunk)
                    }
                })

                <span class="hljs-built_in">this</span>._writeStream.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
                    form.emit(<span class="hljs-string">'error'</span>, e)
                });

                <span class="hljs-comment">// upload to S3</span>
                <span class="hljs-keyword">new</span> Upload({
                    <span class="hljs-attr">client</span>: <span class="hljs-keyword">new</span> S3Client({
                        <span class="hljs-attr">credentials</span>: {
                            accessKeyId,
                            secretAccessKey
                        },
                        region
                    }),
                    <span class="hljs-attr">params</span>: {
                        <span class="hljs-attr">ACL</span>: <span class="hljs-string">'public-read'</span>,
                        Bucket,
                        <span class="hljs-attr">Key</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now().toString()}</span>-<span class="hljs-subst">${<span class="hljs-built_in">this</span>.originalFilename}</span>`</span>,
                        <span class="hljs-attr">Body</span>: <span class="hljs-built_in">this</span>._writeStream
                    },
                    <span class="hljs-attr">tags</span>: [], <span class="hljs-comment">// optional tags</span>
                    <span class="hljs-attr">queueSize</span>: <span class="hljs-number">4</span>, <span class="hljs-comment">// optional concurrency configuration</span>
                    <span class="hljs-attr">partSize</span>: <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">5</span>, <span class="hljs-comment">// optional size of each part, in bytes, at least 5MB</span>
                    <span class="hljs-attr">leavePartsOnError</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// optional manually handle dropped parts</span>
                })
                    .done()
                    .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
                        form.emit(<span class="hljs-string">'data'</span>, { <span class="hljs-attr">name</span>: <span class="hljs-string">"complete"</span>, <span class="hljs-attr">value</span>: data });
                    }).catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
                        form.emit(<span class="hljs-string">'error'</span>, err);
                    })
            }

        })


    })
}

<span class="hljs-built_in">module</span>.exports = parsefile;
</code></pre>
<p>The <code>done</code> method chained to the <code>Upload</code> instance returns a promise when the upload process is complete. </p>
<p>If the upload is successful, the promise resolves with an object containing information about the uploaded file. The <code>form</code> instance emits a <code>data</code> event with the <code>complete</code> name and the returned data. The emitted event sends a successful response to the client. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screen-Shot-2023-04-24-at-10.42.23-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Successful upload response</em></p>
<p>The response data has relevant details that pertain to the upload, including:</p>
<ul>
<li><code>$metadata</code>: An object containing the status code, the number of upload attempts, and so on.</li>
<li><code>Key</code>: The name of the file.</li>
<li><code>Location</code>: A URL pointing to the file's location. It also downloads it.</li>
</ul>
<p>If the upload is unsuccessful, it rejects the promise with an error message and the <code>form</code> instance triggers an <code>error</code> event.</p>
<p>You also want to ensure that every chunk of data is written to the stream before it closes. Modify the <code>file.end()</code> method to ensure the write stream emits a <code>finish</code> event before calling its <code>end</code> method. The <code>finish</code> event signifies that the program has written all the data to the stream, and the stream has closed. And the <code>end</code> event on the stream object indicates that the stream has ended.</p>
<p>The updated <code>fileparser.js</code> file:</p>
<pre><code><span class="hljs-keyword">const</span> { Upload } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/lib-storage"</span>);
<span class="hljs-keyword">const</span> { S3Client } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-sdk/client-s3"</span>);
<span class="hljs-keyword">const</span> Transform = <span class="hljs-built_in">require</span>(<span class="hljs-string">'stream'</span>).Transform;

<span class="hljs-keyword">const</span> accessKeyId = process.env.AWS_ACCESS_KEY_ID;
<span class="hljs-keyword">const</span> secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
<span class="hljs-keyword">const</span> region = process.env.S3_REGION;
<span class="hljs-keyword">const</span> Bucket = process.env.S3_BUCKET;

<span class="hljs-keyword">const</span> parsefile = <span class="hljs-keyword">async</span> (req) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">let</span> options = {
            <span class="hljs-attr">maxFileSize</span>: <span class="hljs-number">100</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">//100 MBs converted to bytes,</span>
            <span class="hljs-attr">allowEmptyFiles</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-keyword">const</span> form = formidable(options);

        form.parse(req, <span class="hljs-function">(<span class="hljs-params">err, fields, files</span>) =&gt;</span> {});

        form.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
            reject(error.message)
        })

        form.on(<span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (data.name === <span class="hljs-string">"successUpload"</span>) {
                resolve(data.value);
            }
        })

        form.on(<span class="hljs-string">'fileBegin'</span>, <span class="hljs-function">(<span class="hljs-params">formName, file</span>) =&gt;</span> {

            file.open = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
                <span class="hljs-built_in">this</span>._writeStream = <span class="hljs-keyword">new</span> Transform({
                    transform(chunk, encoding, callback) {
                        callback(<span class="hljs-literal">null</span>, chunk)
                    }
                })

                <span class="hljs-built_in">this</span>._writeStream.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
                    form.emit(<span class="hljs-string">'error'</span>, e)
                });

                <span class="hljs-comment">// upload to S3</span>
                <span class="hljs-keyword">new</span> Upload({
                    <span class="hljs-attr">client</span>: <span class="hljs-keyword">new</span> S3Client({
                        <span class="hljs-attr">credentials</span>: {
                            accessKeyId,
                            secretAccessKey
                        },
                        region
                    }),
                    <span class="hljs-attr">params</span>: {
                        <span class="hljs-attr">ACL</span>: <span class="hljs-string">'public-read'</span>,
                        Bucket,
                        <span class="hljs-attr">Key</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now().toString()}</span>-<span class="hljs-subst">${<span class="hljs-built_in">this</span>.originalFilename}</span>`</span>,
                        <span class="hljs-attr">Body</span>: <span class="hljs-built_in">this</span>._writeStream
                    },
                    <span class="hljs-attr">tags</span>: [], <span class="hljs-comment">// optional tags</span>
                    <span class="hljs-attr">queueSize</span>: <span class="hljs-number">4</span>, <span class="hljs-comment">// optional concurrency configuration</span>
                    <span class="hljs-attr">partSize</span>: <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">5</span>, <span class="hljs-comment">// optional size of each part, in bytes, at least 5MB</span>
                    <span class="hljs-attr">leavePartsOnError</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// optional manually handle dropped parts</span>
                })
                    .done()
                    .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
                        form.emit(<span class="hljs-string">'data'</span>, { <span class="hljs-attr">name</span>: <span class="hljs-string">"complete"</span>, <span class="hljs-attr">value</span>: data });
                    }).catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
                        form.emit(<span class="hljs-string">'error'</span>, err);
                    })
            }

            file.end = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">cb</span>) </span>{
                <span class="hljs-built_in">this</span>._writeStream.on(<span class="hljs-string">'finish'</span>, <span class="hljs-function">() =&gt;</span> {
                    <span class="hljs-built_in">this</span>.emit(<span class="hljs-string">'end'</span>)
                    cb()
                })
                <span class="hljs-built_in">this</span>._writeStream.end()
            }

        })


    })
}

<span class="hljs-built_in">module</span>.exports = parsefile;
</code></pre><p>An easier-to-read update could involve abstracting the S3 upload process and calling it within the <code>fileparser.js</code> file.</p>
<p>Finally, update the <code>index.js</code> file with the upload route. Import the <code>fileparser</code> module and create a <code>POST</code> route. The module is a promise, which means you can handle the response with  <code>.then()</code> and errors using <code>.catch()</code>.</p>
<p>The updated <code>index.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();

app.set(<span class="hljs-string">'json spaces'</span>, <span class="hljs-number">5</span>); <span class="hljs-comment">// to pretify json response</span>

<span class="hljs-keyword">const</span> PORT = process.env.PORT;
<span class="hljs-keyword">const</span> fileparser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./fileparser'</span>);

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">`
    &lt;h2&gt;File Upload With &lt;code&gt;"Node.js"&lt;/code&gt;&lt;/h2&gt;
    &lt;form action="/api/upload" enctype="multipart/form-data" method="post"&gt;
      &lt;div&gt;Select a file: 
        &lt;input name="file" type="file" /&gt;
      &lt;/div&gt;
      &lt;input type="submit" value="Upload" /&gt;
    &lt;/form&gt;

  `</span>);
});

app.post(<span class="hljs-string">'/api/upload'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">await</span> fileparser(req)
  .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
    res.status(<span class="hljs-number">200</span>).json({
      <span class="hljs-attr">message</span>: <span class="hljs-string">"Success"</span>,
      data
    })
  })
  .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
    res.status(<span class="hljs-number">400</span>).json({
      <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred."</span>,
      error
    })
  })
});

app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${PORT}</span>.`</span>);
})
</code></pre>
<p>And there you have it! </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you have seen how to set up your Node.js application to handle file upload. You also saw how the <code>formidable</code> package facilitates the process using events and how to set up the AWS-SDK <code>Upload</code> module.</p>
<p>This <a target="_blank" href="https://github.com/Fiyin-Anne/node-s3-upload">repo</a> contains the code. At the time of writing, the code logic in the samples here is the same as the repo's content.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
