-
카테고리
-
세부 분야
프론트엔드
-
해결 여부
미해결
q-upload 문의 드립니다.
24.05.10 16:51 작성 조회수 56
0
안녕하세요
quasar 강좌를 듣고
파일 업로드 관련하여 q-upload 를 사용 하여 파일 업로드를 진행 하였습니다.
백엔드는 fastapi 를 사용 하였으며
소스는 아래와 같습니다.
<q-uploader
class="full-width"
label="첨부파일 (for <78k size)"
no-thumbnails
v-model="files"
accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx, .mp4"
multiple
hide-upload-btn
auto-upload
:factory="handleFileAdded"
:upload_progress-label="uploadProgress"
@rejected="onRejected"
@removed="handleFileRemoved"
/>
<script setup> 부분
const files = ref([]); // 업로드된 파일 목록을 담을 배열
const uploadFile2 = async (file, uploadId, fileKey) => {
const formData = new FormData();
formData.append('file', file);
formData.append('uploadId', uploadId);
formData.append('fileKey', fileKey);
try {
const response = await axios.post(
`http://127.0.0.1:8000/api/v1/attachFile/Create`,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${localStorage.getItem('auth/access_token')}`,
},
onUploadProgress: function (progressEvent) {
const { loaded, total } = progressEvent;
const progress = Math.round((loaded * 100) / total);
uploadProgress.value = progress;
console.log('uploadProgress.value', uploadProgress.value);
console.log(`Upload Progress: ${progress}%`);
},
},
);
return response.data;
} catch (error) {
console.error('Error uploading file:', error);
throw error;
}
};
const handleFileAdded = async newFiles => {
for (const file of newFiles) {
try {
const attachFile = await uploadFile2(file, uploadId, file.__key);
console.log(attachFile.attach_id);
console.log(`File uploaded successfully:`, attachFile);
files.value.push(file);
} catch (error) {
console.error(`Error uploading file ${file.name}:`, error);
}
}
};
실행 하면 파일은 정상적으로 백엔드 서버에 등록되고
등록된 파일 정보도 리턴되어 잘 받아 집니다.
하지만 주의 표시아이콘과 파일 업로드 상태가 0% 로 표시 됩니다.
선배님들의 조언 부탁 드립니다.
감사합니다.
답변을 작성해보세요.
0
인프런 AI 인턴
2024.05.12
안녕하세요, 인프런 AI 인턴입니다.
파일 업로드 상태가 0%로 표시되고 주의 표시 아이콘이 나타나는 문제는 q-uploader
의 파일 업로드 상태 업데이트 로직에서 발생할 가능성이 높습니다. q-uploader
컴포넌트는 업로드 진행 상태를 반영하기 위해 내부적으로 상태 관리를 수행합니다. 사용자 정의 업로드 함수에서 업로드 진행 상태를 q-uploader
에 제대로 반영하지 않으면 업로드 진행률이 0%로 표시될 수 있습니다.
q-uploader
의 :factory
prop에 할당된 handleFileAdded
함수 내부에서 업로드 진행률을 관리하는 로직을 확인해보세요. 업로드 진행 상태(progress)를 q-uploader
컴포넌트에 반영하기 위해서는 factory
함수에서 업로드 진행률을 업데이트하는 방식을 q-uploader
에 맞게 구현해야 합니다.
실질적으로 업로드 진행 상태를 업데이트하기 위해서는 uploadProgress
변수를 적절히 업데이트하고 있지만, 이 업데이트가 q-uploader
에서 인식할 수 있는 방식으로 연동되어야 합니다. 공식 문서나 q-uploader
의 구현 예제를 참조하여 업로드 진행률을 제대로 반영하는 방법을 확인해 보세요.
또한, onUploadProgress
이벤트 핸들러가 올바르게 설정되어 있음에도 불구하고 업로드 상태가 반영되지 않는다면, 업로드 진행 상태 업데이트 로직에 문제가 있을 수 있으므로 해당 부분의 코드를 다시 한번 검토해 보시기 바랍니다.
질문하신 내용에 직접적으로 대답할 수 있는 정보가 제공된 문서가 없어 정확한 해결 방안을 제시하기는 어렵지만, 일반적인 조언을 드리자면, q-uploader
의 사용 방법과 관련된 공식 문서를 참고하시거나 Quasar 관련 커뮤니티나 포럼에서 유사한 사례를 찾아보시는 것이 도움이 될 수 있습니다.
조언이 도움이 되었길 바랍니다. 추가적인 질문이 있으시면 언제든지 문의해 주세요.
joohyun kim
질문자2024.05.13
전체 소스 첨부 합니다.
첨부파일 부분 q-upload 관련 해서 개별 항목의 Status: failed 가 출력되고
전체 항목부분에도 0.00% 로 출력됩니다.
페이지 전체 소스 첨부 합니다.
선배님들의 의견 부탁 드립니다.
<template>
<q-form @submit.prevent="handleSubmit">
<q-card-section class="q-gutter-y-sm">
<q-input
v-model="titleModel"
outlined
placeholder="제목"
:rules="[validateRequired]"
counter
maxlength="40"
/>
<q-select
v-model="isActive"
outlined
:options="options"
emit-value
map-options
option-value="value"
option-label="label"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey-7"> 선택하세요. </q-item-section>
</q-item>
</template>
</q-select>
<!-- <q-input
v-model="contentModel"
type="textarea"
outlined
placeholder="내용을 작성해주세요~!"
/> -->
<TiptapEditor v-model="contentModel" />
<q-uploader
class="full-width"
multiple
hide-upload-btn
auto-upload
:factory="handleFileAdded"
@rejected="onRejected"
@removed="handleFileRemoved"
>
<template v-slot:list="scope">
<q-list separator>
<q-item v-for="file in scope.files" :key="file.__key">
<q-item-section>
<q-item-label class="full-width ellipsis">
{{ file.name }}
</q-item-label>
<q-item-label caption>
Status: {{ file.__status }}
</q-item-label>
<q-item-label caption>
{{ file.__sizeLabel }} / {{ file.__progressLabel }}
</q-item-label>
</q-item-section>
<q-item-section v-if="file.__img" thumbnail class="gt-xs">
<img :src="file.__img.src" />
</q-item-section>
<q-item-section top side>
<q-btn
class="gt-xs"
size="12px"
flat
dense
round
icon="delete"
@click="scope.removeFile(file)"
/>
</q-item-section>
</q-item>
</q-list>
</template>
</q-uploader>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<slot name="actions">
<q-btn flat label="취소하기" v-close-popup />
<q-btn type="submit" flat label="저장하기" color="primary" />
</slot>
</q-card-actions>
</q-form>
</template>
<script setup>
import { ref, computed } from 'vue';
import { validateRequired } from 'src/utils/validate-rules';
import TiptapEditor from 'src/components/tiptap/TiptapEditor.vue';
import { uploadFile, removeFile } from 'src/services';
import { useQuasar } from 'quasar';
import { v4 as uuidv4 } from 'uuid';
import axios from 'axios';
const $q = useQuasar();
const uploadId = uuidv4();
console.log(uploadId);
const props = defineProps({
listTitle: {
type: String,
},
listContent: {
type: String,
},
isActive: {
type: Boolean,
},
});
const options = ref([
{ label: '공개', value: true },
{ label: '비밀글', value: false },
]);
const emit = defineEmits([
'update:listTitle',
'update:listContent',
'update:isActive',
'submit',
]);
const titleModel = computed({
get: () => props.listTitle,
set: val => emit('update:listTitle', val),
});
const contentModel = computed({
get: () => props.listContent,
set: val => emit('update:listContent', val),
});
const isActive = computed({
get: () => props.isActive,
set: val => emit('update:isActive', val),
});
const handleSubmit = () => {
if (!contentModel.value) {
$q.notify('내용을 작성하세요.');
return;
}
emit('submit');
};
const onRejected = rejectedEntries => {
console.log('Rejected files:', rejectedEntries);
$q.notify({
type: 'negative',
message: `${rejectedEntries.length} file(s) were rejected due to size or type constraints`,
});
};
const files = ref([]); // 업로드된 파일 목록을 담을 배열
const uploadProgress = ref('0%');
const isUploading = ref(false);
const checkFileSize = files => {
return files.filter(file => file.size < 78000);
};
const updateFileStatus = (file, status, uploadedSize) => {
console.log(`File ${file.name} status updated to: ${status}`);
console.log(`Uploaded size: ${uploadedSize}`);
file.__status = status;
file.__uploaded = uploadedSize;
file.__progress = (uploadedSize / file.size) * 100;
// 여기서 파일 상태를 업데이트하거나 처리할 수 있습니다.
// 예를 들어 Vuex store에 상태를 업데이트하거나 UI를 업데이트하는 등의 작업을 수행할 수 있습니다.
};
const uploadFile2 = async (file, uploadId, fileKey) => {
const formData = new FormData();
formData.append('file', file);
formData.append('uploadId', uploadId);
formData.append('fileKey', fileKey);
try {
const response = await axios.post(
'http://127.0.0.1:8000/api/v1/attachFile/Create',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${localStorage.getItem('auth/access_token')}`,
},
onUploadProgress: function (progressEvent) {
isUploading.value = true;
const progress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total,
);
uploadProgress.value = `${progress}%`;
// Update file progress
file.__progress = progress;
file.__progressLabel = `${progress}%`;
},
},
);
// Check the server response for success
console.log('response.status::::::', response.status);
if (response.status === 200 && response.data) {
updateFileStatus(file, 'uploaded', file.size); // Set file status to uploaded
} else {
updateFileStatus(file, 'failed', 0); // Set file status to failed
}
return response.data;
} catch (error) {
console.error('Error uploading file:', error);
updateFileStatus(file, 'failed', 0);
throw error;
}
};
const handleFileAdded = async newFiles => {
for (const file of newFiles) {
if (file.size >= 78000) {
// Assuming you have a size limit of 78 KB
$q.notify('File size must be less than 78 KB');
continue; // Skip this file
}
try {
const attachFile = await uploadFile2(file, uploadId, file.__key);
console.log(`File uploaded successfully:`, attachFile);
files.value.push(file);
} catch (error) {
console.error(`Error uploading file ${file.name}:`, error);
}
}
};
const handleFileRemoved = async removedFiles => {
// 파일이 제거될 때 실행되는 함수
console.log('Removed files:', removedFiles);
for (const removedFile of removedFiles) {
try {
// 서버에서 파일 제거
const deleted = await removeFile(removedFile.__key, uploadId);
if (deleted) {
// 서버에서 파일이 성공적으로 삭제된 경우 로컬 상태에서도 해당 파일 제거
const index = files.value.findIndex(
file => file.__key === removedFile.__key,
);
if (index !== -1) {
files.value.splice(index, 1);
}
console.log(
`File ${removedFile.name} removed successfully from server.`,
);
} else {
console.error(`Error removing file ${removedFile.name} from server.`);
}
} catch (error) {
console.error(
`Error removing file ${removedFile.name} from server:`,
error,
);
}
}
};
</script>
<style lang="scss" scoped></style>
콘솔 로그 정보 입니다.
의견 부탁 드립니다.
감사합니다.
답변 1