Vue JS Image Upload Progress
December 20, 2024 · AI Conversations
### Step 1: Create a Vue Component
Create a Vue component that includes a file input, a progress bar, and methods to handle file uploads.
<template>
<div>
<h2>Image Upload</h2>
<input type="file" @change="onFileChange" accept="image/*" />
<button @click="uploadImage" :disabled="!selectedFile">Upload</button>
<div v-if="uploadProgress > 0" class="progress-bar">
<div
class="progress"
:style="{ width: uploadProgress + '%' }"
></div>
</div>
<p v-if="uploadProgress > 0">{{ uploadProgress }}% uploaded</p>
</div>
</template>
<script>
export default {
data() {
return {
selectedFile: null,
uploadProgress: 0,
};
},
methods: {
onFileChange(event) {
this.selectedFile = event.target.files[0];
},
uploadImage() {
if (!this.selectedFile) return;
const formData = new FormData();
formData.append('file', this.selectedFile);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
this.uploadProgress = Math.round((event.loaded / event.total) * 100);
}
});
xhr.onload = () => {
if (xhr.status === 200) {
console.log('Upload complete:', xhr.responseText);
this.uploadProgress = 0; // Reset progress after successful upload
} else {
console.error('Upload failed:', xhr.statusText);
}
};
xhr.onerror = () => {
console.error('Upload error');
};
xhr.open('POST', 'YOUR_UPLOAD_URL_HERE', true);
xhr.send(formData);
},
},
};
</script>
<style>
.progress-bar {
width: 100%;
background: #f3f3f3;
border-radius: 5px;
margin-top: 10px;
}
.progress {
height: 20px;
background: #4caf50;
border-radius: 5px;
}
</style>
### Step 2: Update Your Server-side Code
Make sure your server-side code is set up to handle file uploads at the specified `YOUR_UPLOAD_URL_HERE`. This could be an API endpoint in your Laravel application, for example.
### Explanation
1. **File Input**: The “ element allows users to select an image file. The `onFileChange` method stores the selected file in the component's data.
2. **Progress Bar**: A progress bar is displayed based on the `uploadProgress` value, which updates as the upload progresses.
3. **XMLHttpRequest**: An `XMLHttpRequest` object is created to handle the file upload. The `upload` property allows you to listen for progress events.
4. **Progress Event**: The `progress` event calculates the percentage of the upload completed and updates `uploadProgress`.
5. **Completion Handling**: When the upload completes successfully, the progress is reset, and you can handle the response accordingly.
### Step 3: Usage
To use this component, simply import and include it in your Vue application. Ensure to replace `YOUR_UPLOAD_URL_HERE` with your actual server endpoint that processes the file upload.
### Note
– Make sure your backend is configured to accept file uploads, and any necessary CSRF tokens are handled if required by your application.
– For improved user experience, you might want to handle additional states like showing a loading spinner during the upload process or error messages if the upload fails.
### Step 1: Create a Vue Component
Below is an updated version of the previous example, now utilizing Axios for the file upload:
<template>
<div>
<h2>Image Upload</h2>
<input type="file" @change="onFileChange" accept="image/*" />
<button @click="uploadImage" :disabled="!selectedFile">Upload</button>
<div v-if="uploadProgress > 0" class="progress-bar">
<div
class="progress"
:style="{ width: uploadProgress + '%' }"
></div>
</div>
<p v-if="uploadProgress > 0">{{ uploadProgress }}% uploaded</p>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
selectedFile: null,
uploadProgress: 0,
};
},
methods: {
onFileChange(event) {
this.selectedFile = event.target.files[0];
},
async uploadImage() {
if (!this.selectedFile) return;
const formData = new FormData();
formData.append('file', this.selectedFile);
try {
await axios.post('YOUR_UPLOAD_URL_HERE', formData, {
onUploadProgress: (progressEvent) => {
const percentage = Math.round((progressEvent.loaded * 100) / progressEvent.total);
this.uploadProgress = percentage;
},
});
console.log('Upload complete');
this.uploadProgress = 0; // Reset progress after successful upload
} catch (error) {
console.error('Upload failed:', error.response || error.message);
}
},
},
};
</script>
<style>
.progress-bar {
width: 100%;
background: #f3f3f3;
border-radius: 5px;
margin-top: 10px;
}
.progress {
height: 20px;
background: #4caf50;
border-radius: 5px;
}
</style>
### Step 2: Update Your Server-side Code
Ensure that your server-side code is prepared to handle file uploads at the specified `YOUR_UPLOAD_URL_HERE`, which could be an API endpoint in your Laravel application.
### Explanation
1. **File Input**: The “ element allows users to select an image file. The `onFileChange` method stores the selected file.
2. **Progress Bar**: A progress bar shows the upload percentage, which updates as the upload progresses.
3. **Axios POST Request**: The `axios.post` method sends the `formData` containing the file to your server. The `onUploadProgress` option is used to track the upload progress.
4. **Progress Calculation**: The `progressEvent` provides information about the upload, allowing you to calculate and set the `uploadProgress`.
5. **Error Handling**: If the upload fails, the error is logged to the console.
### Step 3: Usage
Import and include this component in your Vue application, replacing `YOUR_UPLOAD_URL_HERE` with your actual upload endpoint.
### Note
– Ensure your backend supports file uploads and is correctly configured to handle requests from your Vue.js application, including any necessary authentication tokens if needed.
.post(props.uploadUrl, formData, {
onUploadProgress: (progressEvent) => {
const total = progressEvent.total;
const loaded = progressEvent.loaded;
console.log('Loaded:', loaded, 'Total:', total);
if (total > 0) {
const progress = Math.round((loaded * 100) / total);
console.log('Progress:', fileData.progress); // Log progress
const index = files.value.indexOf(fileData);
if (index !== -1) {
files.value[index] = { …fileData, progress }; // Update to a new object
}
}
},
})
.then((response) => {
console.log("Upload successful:", response.data);
// fileData.progress = 100;
fileData.path = response.data.path; // Add the file path
// Update fileData to reflect completion
const index = files.value.indexOf(fileData);
if (index !== -1) {
files.value[index] = { …fileData, progress: 100, path: response.data.path };
}
emit("fileUploaded", response.data); // Emit event with file data
})
.catch((error) => {
console.error("File upload failed:", error);
});
modify above
axios
.post(props.uploadUrl, formData, {
onUploadProgress: (progressEvent) => {
const total = progressEvent.total;
const loaded = progressEvent.loaded;
console.log('Loaded:', loaded, 'Total:', total);
if (total > 0) {
const progress = Math.round((loaded * 100) / total);
console.log('Progress:', progress); // Log progress
// Find the index of the current fileData in the files array
const index = files.value.indexOf(fileData);
if (index !== -1) {
// Update the progress of the corresponding fileData object
files.value[index] = { ...fileData, progress }; // Update to a new object with updated progress
}
}
},
})
.then((response) => {
console.log("Upload successful:", response.data);
// Update the fileData with the new path and progress
const index = files.value.indexOf(fileData);
if (index !== -1) {
files.value[index] = {
...fileData,
progress: 100, // Mark progress as complete
path: response.data.path, // Add the file path from the response
};
}
emit("fileUploaded", response.data); // Emit event with file data
})
.catch((error) => {
console.error("File upload failed:", error);
});
### Key Modifications and Explanations
1. **Logging Progress**: The log for the progress has been updated to `console.log('Progress:', progress);` to ensure it displays the correct current progress percentage.
2. **Finding the Index**: The code correctly finds the index of `fileData` in the `files.value` array. This ensures that you are updating the correct file's progress.
3. **Updating File Data**: When updating `files.value`, we create a new object with the updated `progress` and `path` fields. This helps in ensuring reactivity in Vue.js.
4. **Progress Completion**: After a successful upload, the `progress` is set to `100`, indicating that the file upload is complete.
5. **Emitting Event**: The event `fileUploaded` is emitted with the response data once the file upload is successful, which allows parent components to respond accordingly.
### Additional Considerations
– **Error Handling**: Make sure to provide user feedback for failed uploads, such as showing an error message in the UI.
– **State Management**: Depending on your state management solution (like Vuex), you might want to update the state instead of directly modifying the `files` array if it affects shared state.
– **User Experience**: Consider adding visual indicators (like a spinner or success message) in your UI to improve user experience during file uploads.
### Complete Working Example
Here's a more comprehensive example of how to handle file uploads with progress tracking using Axios in a Vue.js component.
<template>
<div>
<h2>Image Upload</h2>
<input type="file" @change="onFileChange" accept="image/*" multiple />
<button @click="uploadImages" :disabled="files.length === 0">Upload</button>
<div v-for="(fileData, index) in files" :key="index" class="file-upload">
<p>{{ fileData.name }}: {{ fileData.progress }}%</p>
<div class="progress-bar">
<div class="progress" :style="{ width: fileData.progress + '%' }"></div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
files: [], // Array to hold file data
};
},
methods: {
onFileChange(event) {
const selectedFiles = Array.from(event.target.files);
this.files = selectedFiles.map(file => ({
name: file.name,
file, // Store the actual file object
progress: 0, // Initialize progress to 0
path: null, // Path will be set after upload
}));
},
async uploadImages() {
const uploadPromises = this.files.map(fileData => {
const formData = new FormData();
formData.append('file', fileData.file);
return axios.post(this.props.uploadUrl, formData, {
onUploadProgress: (progressEvent) => {
const total = progressEvent.total;
const loaded = progressEvent.loaded;
if (total > 0) {
const progress = Math.round((loaded * 100) / total);
console.log(`Progress for ${fileData.name}:`, progress);
// Update the progress for the specific fileData
const index = this.files.indexOf(fileData);
if (index !== -1) {
this.$set(this.files, index, { ...fileData, progress }); // Update file data reactively
}
}
},
})
.then((response) => {
console.log("Upload successful:", response.data);
const index = this.files.indexOf(fileData);
if (index !== -1) {
// Update the path and set progress to 100 after successful upload
this.$set(this.files, index, { ...fileData, progress: 100, path: response.data.path });
}
this.$emit("fileUploaded", response.data); // Emit event with file data
})
.catch((error) => {
console.error("File upload failed:", error);
});
});
// Optional: Wait for all uploads to complete
await Promise.all(uploadPromises);
},
},
};
</script>
<style>
.file-upload {
margin-top: 10px;
}
.progress-bar {
width: 100%;
background: #f3f3f3;
border-radius: 5px;
}
.progress {
height: 20px;
background: #4caf50;
border-radius: 5px;
}
</style>
### Key Points to Ensure Functionality
1. **File Handling**:
– Ensure that your file input allows multiple file selection if needed. You can do this by adding the `multiple` attribute.
– When files are selected, they are mapped to an array of objects containing the file information and an initial progress of 0.
2. **Reactive Updates**:
– Use `this.$set(this.files, index, { …fileData, progress });` to ensure that Vue reactivity is maintained when updating the progress of individual file uploads.
3. **Error Handling**:
– Log any errors that occur during the upload process for easier debugging.
4. **Backend URL**:
– Make sure `this.props.uploadUrl` points to a valid endpoint that handles file uploads.
5. **CORS Issues**:
– If you're uploading to a different domain, ensure CORS is configured correctly on your server to allow requests from your frontend application.
6. **Check Network Requests**:
– Use the browser's developer tools (usually under the Network tab) to check if the requests are being sent correctly and if the server is responding as expected.
7. **File Size Limits**:
– Make sure your backend can handle the file sizes you’re uploading. Some configurations might limit the file upload size.
### Debugging Steps
If the upload is still not working as expected:
1. **Console Logs**: Check the console for any logs or error messages to get more insight into what might be going wrong.
2. **Network Tab**: Inspect the network tab to see if the requests are being made, and examine the request/response details.
3. **Backend Logs**: Look at your backend server logs to see if the requests are reaching the server and if any errors are being logged there.
4. **Browser Compatibility**: Ensure you're testing in a compatible browser. Some older browsers may have issues with certain features.
5. **Reactivity Issues**: If the UI isn’t updating correctly, check that your data structure is reactive and that Vue is aware of the changes.
By following these guidelines and using the complete example, you should be able to get file uploads with progress tracking working in your Vue.js application using Axios.