File Uploads in JavaScript: Guide to the Fetch API

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:

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.


Latest blog posts

Explore the world of programming and cybersecurity through our curated collection of blog posts. From cutting-edge coding trends to the latest cyber threats and defense strategies, we've got you covered.