Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

gidaban79's avatar

Upload multiple files - prevent to upload already uploaded files

Hello guys, hope you are okay, I ma facing some issue with my code

<script setup>
import {defineProps, onMounted, ref, watch} from 'vue';
import axios from 'axios';
import {ArrowUpOnSquareIcon, TrashIcon} from "@heroicons/vue/24/solid";

const emit = defineEmits(['pendingUploads']);

let props = defineProps({
    url: {
        type: String,
        Required: true,
    },
    maxSize: {
        type: Number,
        default: 5000000
    },
    acceptedTypes: {
        type: Array,
        default: () => ['image/png', 'image/jpeg', 'image/jpg'],
    },
    skipExistingFiles: {
        type: Boolean,
        default: false,
    }
});

let files = ref([]);
let totalProgress = ref(0);
let uploadSpeed = ref(0);
let errorMessage = ref('');
let isDragOver = ref(false);
let displayError = ref(true);
let canUpload = ref(false);
let fileInput = ref(null);
let isUploading = ref(false);
let uploadedFileIds = ref([]);

const calculateTotalProgress = () => {
    const totalCompleted = files.value.reduce((acc, file) => acc + (file.progress || 0), 0);
    const validFiles = files.value.filter(file => !file.error);
    totalProgress.value = validFiles.length ? totalCompleted / validFiles.length : 0;
};
const handleFiles = async (eventFiles) => {

    files.value = [...files.value, ...[...eventFiles].map((file, index) => {
        if (file.size > props.maxSize) {
            file.errorMessage = 'File size exceeds the maximum limit.';
            errorMessage.value = file.errorMessage;
            file.error = true;
            file.uuid = Math.random().toString(36).substring(7);
            return file;
        }
        if (!props.acceptedTypes.includes(file.type)) {
            file.errorMessage = 'File type not supported.';
            errorMessage.value = file.errorMessage;
            file.error = true;
            return file;
        }

        file.previewUrl = URL.createObjectURL(file);
        file.uploading = false;
        file.progress = 0;

        calculateTotalProgress();
        return file;
    })];
    canUpload.value = true;
    emit('pendingUploads', true);
};
const uploadFile = async (file) => {
    const startTime = new Date().getTime();
    file.uploading = true;
    file.error = false;
    const formData = new FormData();
    formData.append('file', file);
    file.progress = 0;

    await axios.post(props.url, formData, {
        onUploadProgress: progressEvent => {
            file.progress = Number(((progressEvent.loaded / progressEvent.total * 100).toFixed(2)));
            calculateTotalProgress();
            const elapsedTime = new Date().getTime() - startTime;
            uploadSpeed.value = ((progressEvent.loaded / (1024 * 1024)) / (elapsedTime / 1000)).toFixed(2);
        }
    })
        .then((response) => {
            file.uploading = false;
            file.uploaded = true;
            file.success = true;
            file.id = response.data.id;
            calculateTotalProgress();
            uploadedFileIds.value.push(response.data.id);
            console.log(uploadedFileIds.value);
        })
        .catch((error) => {
            file.uploading = false;
            file.error = true;
            errorMessage.value = error.message;
            calculateTotalProgress();
        });

};
const dropHandler = (e) => {
    e.preventDefault();
    isDragOver.value = false;
    const droppedFiles = e.dataTransfer.files;
    handleFiles(droppedFiles);
    e.target.value = null;
};

const dragOverHandler = (e) => {
    e.preventDefault();
    isDragOver.value = true;
};

const dragLeaveHandler = (e) => {
    e.preventDefault();
    isDragOver.value = false;
};

onMounted(() => emit('filesPending', false));
watch(files, () => {
    if (!files.value.length) {
        emit('pendingUploads', false);
    }
});

const uploadFiles = async () => {
    // Only upload if canUpload is true and not already uploading
    if (canUpload.value && !isUploading.value) {
        isUploading.value = true; // Set flag to indicate upload in progress
        for (const file of files.value) {
            if (!file.error) {
                await uploadFile(file);
            }
        }
        isUploading.value = false; // Reset flag when upload is complete
    }
};

const deleteFile = (index) => {
    files.value.splice(index, 1);
};
const deleteAllFiles = () => {
    files.value = [];
    canUpload.value = false;
};
const openFilePicker = () => {
    if (fileInput.value) {
        fileInput.value.click();
    }
};
watch(errorMessage, () => {
    setTimeout(() => {
        displayError.value = false;
    }, 5000);
});
</script>

<template>
    <div class="w-1/2 justify-center flex-row mx-auto">
        <transition name="slide-fade">
            <div v-if="errorMessage && displayError"
                 class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 round mx-2 my-2"
                 role="alert">
                <strong class="font-bold">Error! </strong>
                <span class="block sm:inline">
                    {{ errorMessage }}
                </span>
            </div>
        </transition>
        <div
            @drop="dropHandler"
            @dragover="dragOverHandler"
            @dragleave="dragLeaveHandler"
            @click="openFilePicker"
            :class="{'bg-gray-300': isDragOver}"
            class="m-4 p-4 border-2 border-gray-300"
        >

            <div class="grid grid-cols-4 gap-4">
                <div
                    v-for="(file, index) in files"
                    :key="index"
                    :data-file-name="file.name"
                    :class="{'bg-orange-500': file.error, 'bg-gray-100': !file.uploading && !file.error, 'border-red-600': file.error, 'border-gray-300': !file.error, 'border-b-fuchsia-950': file.success}"
                    class="grid grid-cols-1 m-2 p-2 border rounded-md items-center justify-between">

                    <img v-if="props.acceptedTypes.includes(file.type)"
                         :data-file-name="file.name"
                         :src="file.previewUrl" alt="Image preview"
                         class="h-auto w-full object-cover rounded"/>
                    <span class="self-center text-sm text-center mt-2 text-gray-500">
                        {{ file.name }}
                    </span>
                    <div class="flex items-center">
                        <progress :value="file.progress" max="100" class="w-full rounded-full"></progress>
                        <button @click="deleteFile(index)"
                                class="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-1 rounded w-8 h-8 ml-2">
                            <TrashIcon class="h-5 w-5 inline-block"/>
                        </button>
                    </div>
                </div>
            </div>
        </div>
        <input type="file" multiple ref="fileInput" @change="handleFiles($event.target.files)" class="opacity-0">
        <div class="flex m-2 p-2 items-center">
            <span class="ml-2">Upload speed: {{ uploadSpeed }} MBps</span>
            <button @click="uploadFiles"
                    :disabled="!canUpload"
                    class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                <ArrowUpOnSquareIcon class="h-5 w-5 inline-block"/>
            </button>

            <button @click="deleteAllFiles"
                    class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded ml-2">
                <TrashIcon class="h-5 w-5 inline-block"/>
            </button>

            <progress :value="totalProgress" max="100" class="ml-2 w-full rounded-md"></progress>
        </div>
    </div>
</template>

<style scoped>
.bg-gray-300 {
    background-color: #e5e7eb;
}

.w-full {
    width: 100%;
}

.h-full {
    height: 100%;
}

.opacity-0 {
    opacity: 0;
}

:deep(.bg-orange-500) {
    background-color: #FFA500;
}
</style>

upload works perfect, but after upload 2 files i will add new file and try upload again, All files will be upload again, i mean 2 old files and new one.

0 likes
1 reply
Tray2's avatar

You most likely need to reset the file input after the upload.

Please or to participate in this conversation.