Skip to content

File Uploads in JavaScript: Guide to the Fetch API

A complete guide to uploading files in JavaScript using the Fetch API and FormData. Learn best practices, see a full code example with progress bar, and understand error handling.

Uploading files is a fundamental feature for modern web applications, from sharing photos to submitting documents. While the XMLHttpRequest object has long been the standard, the modern JavaScript Fetch API offers a cleaner, promise-based approach that integrates seamlessly with contemporary web development practices . This guide will walk you through uploading files using fetch and FormData, incorporating best practices for security, error handling, and user experience .

The Core: FormData and Fetch

The cornerstone of file uploads with fetch is the FormData interface . This powerful object allows you to construct a set of key/value pairs representing form fields and their values, making it ideal for sending files to a server . When you send a FormData object as the request body, the browser automatically sets the correct Content-Type header to multipart/form-data with a unique boundary parameter, which is essential for separating the different parts of the form data . Manually setting this header will break the upload, so it’s crucial to let the browser handle it.

Here’s a complete, working example of a single file upload:

HTML:

<input type="file" id="fileInput" accept=".jpg,.png,.pdf" />
<button id="uploadBtn" onclick="uploadFile()">Upload</button>
<progress id="progressBar" value="0" max="100" style="display: none"></progress>
<div id="status"></div>

JavaScript:

async function uploadFile() {
    const fileInput = document.getElementById("fileInput");
    const file = fileInput.files[0];
    const progressBar = document.getElementById("progressBar");
    const status = document.getElementById("status");
    const uploadBtn = document.getElementById("uploadBtn");

    if (!file) {
        status.textContent = "Please select a file.";
        return;
    }

    // Validate file size (e.g., limit to 5MB)
    const maxSize = 5 * 1024 * 1024; // 5MB in bytes
    if (file.size > maxSize) {
        status.textContent =
            "File is too large. Please select a file under 5MB.";
        return;
    }

    const formData = new FormData();
    formData.append("file", file);

    // Show progress bar
    progressBar.style.display = "block";
    progressBar.value = 0;
    status.textContent = "Uploading...";
    uploadBtn.disabled = true;

    try {
        const response = await fetch(
            "https://your-server-endpoint.com/upload",
            {
                method: "POST",
                body: formData,
            },
        );

        // Hide progress bar
        progressBar.style.display = "none";

        if (response.ok) {
            const result = await response.json();
            status.textContent = `Success! ${result.message}`;
        } else {
            status.textContent = `Upload failed: ${response.statusText}`;
        }
    } catch (error) {
        // Hide progress bar on error
        progressBar.style.display = "none";
        status.textContent = `Network error: ${error.message}`;
    } finally {
        uploadBtn.disabled = false;
    }
}

For multiple files, simply append each file to the FormData object using the same key:

for (const file of fileInput.files) {
    formData.append("files", file); // Creates an array on the server
}

Enhancing User Experience: Progress Tracking

A critical aspect of a good file upload experience is providing feedback, especially for large files. While the standard fetch API does not natively support upload progress events , there are workarounds . One common method involves creating a custom solution using the ReadableStream API to track the number of bytes sent . However, for simpler implementations, many developers still rely on XMLHttpRequest for its built-in onprogress event . Implementing a progress bar significantly improves user experience by giving real-time feedback during the upload process .

Best Practices for Robust Uploads

Following best practices ensures your upload functionality is secure, reliable, and user-friendly . Key practices include:

  • Client-Side Validation: Check file type and size before initiating the upload to prevent unnecessary server requests and potential errors like 415 Unsupported Media Type .
  • Comprehensive Error Handling: Use try...catch blocks to handle network errors and always check the response.ok property to manage HTTP errors effectively .
  • Security: Be aware of potential security risks and implement measures like file type validation on the server side.
  • User Feedback: Provide clear status messages and disable the upload button during the process to prevent duplicate submissions.

Conclusion

By mastering the FormData object and the fetch API, you can build powerful and modern file upload features that are both functional and secure.


Topics & Technologies

JavaScriptfetch APIfile uploadFormDatamultipart form dataupload progressPOST requestweb developmentSEO blogbest practices

Continue Reading

View all →