File Upload API in local HTML file (v2024)

i am building a form that saves as draft . i use the rest Api to insert form data and all works well . The only issue now is how to upload file.
My code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Offline Form with Draft Saving</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input, textarea, select {
            width: 100%;
            padding: 8px;
            box-sizing: border-box;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 10px;
        }
        button:hover {
            background-color: #45a049;
        }
        #status {
            margin-top: 15px;
            padding: 10px;
            border-radius: 4px;
        }
        .online { background-color: #dff0d8; color: #3c763d; }
        .offline { background-color: #f2dede; color: #a94442; }
        .syncing { background-color: #d9edf7; color: #31708f; }
    </style>
</head>
<body>
    <h1>Offline-Capable Form</h1>
    <div id="status" class="offline">Status: Checking connection...</div>
    
    <form id="myForm">
        <div class="form-group">
            <label for="name">Name:</label>
            <input type="text" id="name" name="name" required>
        </div>
        
        <div class="form-group">
            <label for="email">Email:</label>
            <input type="email" id="email" name="email" required>
        </div>
        
        <div class="form-group">
            <label for="message">Message:</label>
            <textarea id="message" name="message" rows="5" required></textarea>
        </div>
        
        <div class="form-group">
            <label for="priority">Priority:</label>
            <select id="priority" name="priority">
                <option value="low">Low</option>
                <option value="medium">Medium</option>
                <option value="high">High</option>
            </select>
        </div>
        <div class="form-group">
            <label for="image">Upload Image:</label>
            <input type="file" id="image" name="image" accept="image/*">
            <img id="imagePreview" alt="Image Preview">
        </div>
        <button type="submit">Submit</button>
        <button type="button" id="saveDraft">Save Draft</button>
        <button type="button" id="loadDraft">Load Draft</button>
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const form = document.getElementById('myForm');
            const saveDraftBtn = document.getElementById('saveDraft');
            const loadDraftBtn = document.getElementById('loadDraft');
            const statusDiv = document.getElementById('status');
            
            // Check online status
            function updateOnlineStatus() {
                if (navigator.onLine) {
                    statusDiv.textContent = 'Status: Online';
                    statusDiv.className = 'online';
                    // Try to sync any pending submissions
                    syncPendingSubmissions();
                } else {
                    statusDiv.textContent = 'Status: Offline - Drafts will be saved locally';
                    statusDiv.className = 'offline';
                }
            }
            
            // Initial check
            updateOnlineStatus();
            
            // Listen for online/offline events
            window.addEventListener('online', updateOnlineStatus);
            window.addEventListener('offline', updateOnlineStatus);
            
            // Save draft to localStorage
            saveDraftBtn.addEventListener('click', function() {
                const formData = {
                    name: form.name.value,
                    email: form.email.value,
                    message: form.message.value,
                    priority: form.priority.value,
                    image: form.image.value,
                    
                    timestamp: new Date().toISOString()
                };
                
                localStorage.setItem('formDraft', JSON.stringify(formData));
                statusDiv.textContent = 'Draft saved locally at ' + new Date().toLocaleTimeString();
                statusDiv.className = 'online';
                
                // Hide the message after 3 seconds
                setTimeout(() => {
                    updateOnlineStatus();
                }, 3000);
            });
            
            // Load draft from localStorage
            loadDraftBtn.addEventListener('click', function() {
                const draft = localStorage.getItem('formDraft');
                if (draft) {
                    const formData = JSON.parse(draft);
                    form.name.value = formData.name || '';
                    form.email.value = formData.email || '';
                    form.message.value = formData.message || '';
                    form.image.value = formData.image || '';
                    form.priority.value = formData.priority || 'low';
                    
                    statusDiv.textContent = 'Draft loaded from ' + new Date(formData.timestamp).toLocaleString();
                    statusDiv.className = 'online';
                    
                    // Hide the message after 3 seconds
                    setTimeout(() => {
                        updateOnlineStatus();
                    }, 3000);
                } else {
                    statusDiv.textContent = 'No draft found in local storage';
                    statusDiv.className = 'offline';
                    setTimeout(() => {
                        updateOnlineStatus();
                    }, 3000);
                }
            });
            
            // Save pending submissions to localStorage when offline
            function savePendingSubmission(formData) {
                let pendingSubmissions = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                pendingSubmissions.push(formData);
                localStorage.setItem('pendingSubmissions', JSON.stringify(pendingSubmissions));
            }
            
            // Sync pending submissions when coming online
            function syncPendingSubmissions() {
                const pendingSubmissions = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                
                if (pendingSubmissions.length > 0) {
                    statusDiv.textContent = 'Syncing ' + pendingSubmissions.length + ' pending submission(s)...';
                    statusDiv.className = 'syncing';
                    
                    // Process each pending submission
                    pendingSubmissions.forEach((submission, index) => {
                        submitFormData(submission)
                            .then(() => {
                                // Remove successfully submitted item
                                const updatedPending = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                                updatedPending.splice(index, 1);
                                localStorage.setItem('pendingSubmissions', JSON.stringify(updatedPending));
                                
                                if (index === pendingSubmissions.length - 1) {
                                    statusDiv.textContent = 'All pending submissions synced successfully!';
                                    statusDiv.className = 'online';
                                    setTimeout(updateOnlineStatus, 3000);
                                }
                            })
                            .catch(error => {
                                console.error('Failed to sync submission:', error);
                                statusDiv.textContent = 'Some submissions failed to sync. Will retry later.';
                                statusDiv.className = 'offline';
                                setTimeout(updateOnlineStatus, 3000);
                            });
                    });
                }
            }
            
            // Submit form data to server
            function submitFormData(formData) {
                return new Promise((resolve, reject) => {
                    fetch('http://localhost/app/api/add/frmdata', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(formData)
                    })
                    .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.json();
                    })
                    .then(data => {
                        console.log('Success:', data);
                        resolve(data);
                    })
                    .catch(error => {
                        console.error('Error:', error);
                        reject(error);
                    });
                });
            }
            
            // Form submission handler
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                
                const formData = {
                    name: form.name.value,
                    email: form.email.value,
                    message: form.message.value,
                    priority: form.priority.value,
                    image: form.image.value,
                    timestamp: new Date().toISOString()
                };
                
                if (navigator.onLine) {
                    // Submit directly if online
                    statusDiv.textContent = 'Submitting form...';
                    statusDiv.className = 'syncing';
                    
                    submitFormData(formData)
                        .then(data => {
                            statusDiv.textContent = 'Form submitted successfully!';
                            statusDiv.className = 'online';
                            form.reset();
                            setTimeout(updateOnlineStatus, 3000);
                        })
                        .catch(error => {
                            statusDiv.textContent = 'Submission failed. Saved as draft.';
                            statusDiv.className = 'offline';
                            savePendingSubmission(formData);
                            setTimeout(updateOnlineStatus, 3000);
                        });
                } else {
                    // Save as pending submission if offline
                    savePendingSubmission(formData);
                    statusDiv.textContent = 'Offline: Submission saved locally. Will sync when online.';
                    statusDiv.className = 'offline';
                    form.reset();
                    setTimeout(updateOnlineStatus, 3000);
                }
            });
            
            // Check for drafts on page load
            const draft = localStorage.getItem('formDraft');
            if (draft) {
                if (confirm('You have a saved draft. Would you like to load it?')) {
                    loadDraftBtn.click();
                }
            }
        });
    </script>
</body>
</html>

You may refer to Upload Files.

This is what i get from console. The token was logged but no file in the upload folder . Even when i uploaded with Swagger UI i got success response but file was not uploaded .

86550715  // token 
// form response
api/:219 Success: {success: true, action: 'add', frmdata: {…}, version: '24.16.0'}action: "add"frmdata: email: "vintoict@gmail.com"id: "14"message: "testing"name: "Omoyeni Olakunle Victor"priority: "low"[[Prototype]]: Objectsuccess: trueversion: "24.16.0"[[Prototype]]: Object

My code

    <form id="myForm">
        <div class="form-group">
            <label for="name">Name:</label>
            <input type="text" id="name" name="name" required>
        </div>
        
        <div class="form-group">
            <label for="email">Email:</label>
            <input type="email" id="email" name="email" required>
        </div>
        
        <div class="form-group">
            <label for="message">Message:</label>
            <textarea id="message" name="message" rows="5" required></textarea>
        </div>
        
        <div class="form-group">
            <label for="priority">Priority:</label>
            <select id="priority" name="priority">
                <option value="low">Low</option>
                <option value="medium">Medium</option>
                <option value="high">High</option>
            </select>
        </div>
        <div class="form-group">
            
            <input type="file" id="id" name="image" onchange="uploadFiles(this);">

        </div>
        <button type="submit">Submit</button>
        <button type="button" id="saveDraft">Save Draft</button>
        <button type="button" id="loadDraft">Load Draft</button>
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const form = document.getElementById('myForm');
            const saveDraftBtn = document.getElementById('saveDraft');
            const loadDraftBtn = document.getElementById('loadDraft');
            const statusDiv = document.getElementById('status');
            
            // Check online status
            function updateOnlineStatus() {
                if (navigator.onLine) {
                    statusDiv.textContent = 'Status: Online';
                    statusDiv.className = 'online';
                    // Try to sync any pending submissions
                    syncPendingSubmissions();
                } else {
                    statusDiv.textContent = 'Status: Offline - Drafts will be saved locally';
                    statusDiv.className = 'offline';
                }
            }
            
            // Initial check
            updateOnlineStatus();
            
            // Listen for online/offline events
            window.addEventListener('online', updateOnlineStatus);
            window.addEventListener('offline', updateOnlineStatus);
            
            // Save draft to localStorage
            saveDraftBtn.addEventListener('click', function() {
                const formData = {
                    name: form.name.value,
                    email: form.email.value,
                    message: form.message.value,
                    priority: form.priority.value,
                    timestamp: new Date().toISOString()
                };
                
                localStorage.setItem('formDraft', JSON.stringify(formData));
                statusDiv.textContent = 'Draft saved locally at ' + new Date().toLocaleTimeString();
                statusDiv.className = 'online';
                
                // Hide the message after 3 seconds
                setTimeout(() => {
                    updateOnlineStatus();
                }, 3000);
            });
            
            // Load draft from localStorage
            loadDraftBtn.addEventListener('click', function() {
                const draft = localStorage.getItem('formDraft');
                if (draft) {
                    const formData = JSON.parse(draft);
                    form.name.value = formData.name || '';
                    form.email.value = formData.email || '';
                    form.message.value = formData.message || '';
                    form.priority.value = formData.priority || 'low';
                    
                    statusDiv.textContent = 'Draft loaded from ' + new Date(formData.timestamp).toLocaleString();
                    statusDiv.className = 'online';
                    
                    // Hide the message after 3 seconds
                    setTimeout(() => {
                        updateOnlineStatus();
                    }, 3000);
                } else {
                    statusDiv.textContent = 'No draft found in local storage';
                    statusDiv.className = 'offline';
                    setTimeout(() => {
                        updateOnlineStatus();
                    }, 3000);
                }
            });
            
            // Save pending submissions to localStorage when offline
            function savePendingSubmission(formData) {
                let pendingSubmissions = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                pendingSubmissions.push(formData);
                localStorage.setItem('pendingSubmissions', JSON.stringify(pendingSubmissions));
            }
            
            // Sync pending submissions when coming online
            function syncPendingSubmissions() {
                const pendingSubmissions = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                
                if (pendingSubmissions.length > 0) {
                    statusDiv.textContent = 'Syncing ' + pendingSubmissions.length + ' pending submission(s)...';
                    statusDiv.className = 'syncing';
                    
                    // Process each pending submission
                    pendingSubmissions.forEach((submission, index) => {
                        submitFormData(submission)
                            .then(() => {
                                // Remove successfully submitted item
                                const updatedPending = JSON.parse(localStorage.getItem('pendingSubmissions')) || [];
                                updatedPending.splice(index, 1);
                                localStorage.setItem('pendingSubmissions', JSON.stringify(updatedPending));
                                
                                if (index === pendingSubmissions.length - 1) {
                                    statusDiv.textContent = 'All pending submissions synced successfully!';
                                    statusDiv.className = 'online';
                                    setTimeout(updateOnlineStatus, 3000);
                                }
                            })
                            .catch(error => {
                                console.error('Failed to sync submission:', error);
                                statusDiv.textContent = 'Some submissions failed to sync. Will retry later.';
                                statusDiv.className = 'offline';
                                setTimeout(updateOnlineStatus, 3000);
                            });
                    });
                }
            }
            
            // Submit form data to server
            function submitFormData(formData) {
                return new Promise((resolve, reject) => {
                    fetch('http://localhost/app/api/add/frmdata', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(formData)
                    })
                    .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.json();
                    })
                    .then(data => {
                        console.log('Success:', data);
                        resolve(data);
                    })
                    .catch(error => {
                        console.error('Error:', error);
                        reject(error);
                    });
                });
            }
            
            // Form submission handler
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                
                const formData = {
                    name: form.name.value,
                    email: form.email.value,
                    message: form.message.value,
                    priority: form.priority.value,
                   
                    timestamp: new Date().toISOString()
                };
                
                if (navigator.onLine) {
                    // Submit directly if online
                    statusDiv.textContent = 'Submitting form...';
                    statusDiv.className = 'syncing';
                    
                    submitFormData(formData)
                        .then(data => {
                            statusDiv.textContent = 'Form submitted successfully!';
                            statusDiv.className = 'online';
                            form.reset();
                            setTimeout(updateOnlineStatus, 3000);
                        })
                        .catch(error => {
                            statusDiv.textContent = 'Submission failed. Saved as draft.';
                            statusDiv.className = 'offline';
                            savePendingSubmission(formData);
                            setTimeout(updateOnlineStatus, 3000);
                        });
                } else {
                    // Save as pending submission if offline
                    savePendingSubmission(formData);
                    statusDiv.textContent = 'Offline: Submission saved locally. Will sync when online.';
                    statusDiv.className = 'offline';
                    form.reset();
                    setTimeout(updateOnlineStatus, 3000);
                }
            });
            
            // Check for drafts on page load
            const draft = localStorage.getItem('formDraft');
            if (draft) {
                if (confirm('You have a saved draft. Would you like to load it?')) {
                    loadDraftBtn.click();
                }
            }
        });

        // Upload files
function uploadFiles(el) {
    let files = el.files, // Get FileList object
        imgname = el.image,
        url = "http://localhost/app/api/upload",
        xhr = new XMLHttpRequest(),
        fd = new FormData();
    xhr.responseType = "json";
    xhr.open("POST", url, true);
    // xhr.setRequestHeader("X-Authorization", "Bearer " + store.JWT); // Set JWT header, if necessary
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200 && xhr.response) { // Files uploaded
            if (xhr.response.filetoken) // Check file token
                console.log(xhr.response.filetoken);
        }
    };
    Array.from(files).forEach(file => fd.append(imgname, file)); // Append File object to form data
    xhr.send(fd);
}
    </script>
</body>
</html>