file upload update
This commit is contained in:
@@ -4,7 +4,6 @@ import React, { useRef, useState, useCallback, ChangeEvent, DragEvent } from 're
|
||||
import { IconShieldAdd } from '@/app/ui/icons/icons';
|
||||
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/file-upload';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { FileExtension } from '@/app/actions/definitions';
|
||||
|
||||
interface SelectedFile {
|
||||
file: File;
|
||||
@@ -27,40 +26,79 @@ interface FileUploadInitResponse {
|
||||
|
||||
interface UploadSectionFile {
|
||||
fileType: string
|
||||
allowedExtensions: FileExtension[]
|
||||
allowedExtensions: string[]
|
||||
maxFileSize: number
|
||||
}
|
||||
|
||||
/* type SupportedFormat = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/bmp'; */
|
||||
|
||||
export default function UploadSectionFile({ fileType, allowedExtensions }: UploadSectionFile) {
|
||||
export default function UploadSectionFile({ fileType, allowedExtensions, maxFileSize }: UploadSectionFile) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [isDragging, setIsDragging] = useState<boolean>(false);
|
||||
const [selectedFile, setSelectedFile] = useState<SelectedFile | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [uploadId, setUploadId] = useState<string | null>(null);
|
||||
const [isFileUploaded, setIsFileUploaded] = useState<boolean>(false);
|
||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
||||
|
||||
const t = useTranslations('Global');
|
||||
console.log('fileType');
|
||||
console.log(allowedExtensions);
|
||||
let SUPPORTED_FORMATS: string[] = []
|
||||
let SUPPORTED_FORMATS: string[] = [];
|
||||
|
||||
if (fileType === "IMAGE") {
|
||||
SUPPORTED_FORMATS = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/bmp'
|
||||
];
|
||||
switch (fileType) {
|
||||
case 'IMAGE':
|
||||
SUPPORTED_FORMATS = [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/bmp',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
'image/tiff',
|
||||
'image/x-icon'
|
||||
];
|
||||
break;
|
||||
case 'VIDEO':
|
||||
SUPPORTED_FORMATS = [
|
||||
'video/mp4',
|
||||
'video/mpeg',
|
||||
'video/ogg',
|
||||
'video/webm',
|
||||
'video/quicktime',
|
||||
'video/x-msvideo',
|
||||
'video/x-matroska',
|
||||
'video/x-flv',
|
||||
'video/3gpp',
|
||||
'video/3gpp2'
|
||||
];
|
||||
break;
|
||||
case 'AUDIO':
|
||||
SUPPORTED_FORMATS = [
|
||||
'audio/mpeg',
|
||||
'audio/wav',
|
||||
'audio/wave',
|
||||
'audio/x-wav',
|
||||
'audio/x-pn-wav',
|
||||
'audio/ogg',
|
||||
'audio/webm',
|
||||
'audio/aac',
|
||||
'audio/mp4',
|
||||
'audio/x-m4a',
|
||||
'audio/flac',
|
||||
'audio/x-flac',
|
||||
'audio/opus'
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const ALLOWED_EXTENSIONS: FileExtension[] = allowedExtensions;
|
||||
const ALLOWED_EXTENSIONS: string[] = allowedExtensions;
|
||||
|
||||
|
||||
const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => {
|
||||
if (!SUPPORTED_FORMATS.includes(file.type as string)) {
|
||||
const extension = file.name.split('.').pop()?.toLowerCase();
|
||||
if (!extension || !ALLOWED_EXTENSIONS.includes(extension as FileExtension)) {
|
||||
if (!extension || !ALLOWED_EXTENSIONS.includes(extension as string)) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: 'Неподдерживаемый формат файла.'
|
||||
@@ -68,11 +106,12 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_SIZE = 100 * 1024 * 1024;
|
||||
const MAX_SIZE = maxFileSize;
|
||||
|
||||
if (file.size > MAX_SIZE) {
|
||||
return {
|
||||
isValid: false,
|
||||
errorMessage: 'Файл слишком большой. Максимальный размер: 100MB'
|
||||
errorMessage: 'Файл слишком большой. Максимальный размер: 1000MB'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,23 +146,28 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadProgress(0);
|
||||
setError(null);
|
||||
|
||||
const validation = validateFile(file);
|
||||
|
||||
if (!validation.isValid) {
|
||||
setError(validation.errorMessage || 'Неизвестная ошибка валидации');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const dimensions = await getImageDimensions(file);
|
||||
let dimensions = undefined;
|
||||
|
||||
if (dimensions.width < 150 || dimensions.height < 150 ||
|
||||
dimensions.width > 8000 || dimensions.height > 8000) {
|
||||
setError(`Размер изображения: ${dimensions.width}x${dimensions.height}. Допустимый диапазон: 150x150 - 4000x4000 пикселей.`);
|
||||
return;
|
||||
if (fileType === "IMAGE") {
|
||||
dimensions = await getImageDimensions(file) as { width: number, height: number };
|
||||
if (dimensions.width < 150 || dimensions.height < 150 ||
|
||||
dimensions.width > 10000 || dimensions.height > 10000) {
|
||||
setError(`Размер изображения: ${dimensions.width}x${dimensions.height}. Допустимый диапазон: 150x150 - 10000x10000 пикселей.`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
@@ -133,14 +177,15 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
preview: objectUrl,
|
||||
name: file.name,
|
||||
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
|
||||
dimensions
|
||||
...(dimensions && { dimensions })
|
||||
});
|
||||
|
||||
console.log('Файл обработан:', {
|
||||
file: file,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
dimensions
|
||||
...(dimensions && { dimensions })
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
@@ -192,13 +237,21 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
setIsFileUploaded(false);
|
||||
setSelectedFile(null);
|
||||
setError(null);
|
||||
setUploadProgress(0);
|
||||
};
|
||||
|
||||
const handlerFileUpload = async (file: SelectedFile): Promise<any> => {
|
||||
const handlerFileUpload = async (file: SelectedFile): Promise<void> => {
|
||||
const extension = file.name.split('.').pop();
|
||||
|
||||
if (!extension) {
|
||||
setError(t('file-has-no-extension'));
|
||||
return;
|
||||
}
|
||||
|
||||
const initMessageBody = {
|
||||
file_name: file.name,
|
||||
file_type: fileType,
|
||||
extension: file.name.split('.')[1],
|
||||
extension: extension,
|
||||
file_size: file.file.size
|
||||
};
|
||||
|
||||
@@ -239,26 +292,26 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
|
||||
start = end;
|
||||
chunkIndex++;
|
||||
setUploadProgress(
|
||||
() => {
|
||||
return Math.floor(chunkIndex / totalChunks * 100);
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setError('Chunk uploaded error ' + error);
|
||||
console.error('Chunk upload error:', error);
|
||||
}
|
||||
} else {
|
||||
console.log(response);
|
||||
setError('file uploaded error');
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (selectedFile) {
|
||||
URL.revokeObjectURL(selectedFile.preview);
|
||||
}
|
||||
};
|
||||
}, [selectedFile]);
|
||||
|
||||
return (
|
||||
<div className="upload-section">
|
||||
<h3>
|
||||
{t('download-for-protection', { fileType: t(fileType.toLocaleLowerCase()) })}
|
||||
{t('upload-for-protection', { fileType: t(fileType.toLocaleLowerCase()) })}
|
||||
</h3>
|
||||
|
||||
<div
|
||||
@@ -349,14 +402,27 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img
|
||||
src={selectedFile.preview}
|
||||
alt="Предпросмотр загруженного изображения"
|
||||
className="uploaded-image"
|
||||
onError={() => setError('Не удалось загрузить превью изображения')}
|
||||
/>
|
||||
{fileType === 'IMAGE' && (
|
||||
<img
|
||||
src={selectedFile.preview}
|
||||
alt="Предпросмотр загруженного изображения"
|
||||
className="uploaded-image"
|
||||
onError={() => setError('Не удалось загрузить превью изображения')}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex gap-3 justify-center">
|
||||
{fileType === 'VIDEO' && (
|
||||
<video
|
||||
src={selectedFile.preview}
|
||||
className="uploaded-image"
|
||||
controls
|
||||
muted
|
||||
onError={() => setError('Не удалось загрузить превью видео')}
|
||||
onLoadedData={() => console.log('Видео загружено')}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-4 flex gap-3 justify-center relative">
|
||||
<button
|
||||
className="btn btn-cancel"
|
||||
onClick={handleClearFile}
|
||||
@@ -365,11 +431,12 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
{isFileUploaded ? t('close') : t('cancel')}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-confirm"
|
||||
className={`btn btn-confirm ${error ? 'disabled' : ''}`}
|
||||
onClick={() => {
|
||||
handlerFileUpload(selectedFile);
|
||||
}}
|
||||
type="button"
|
||||
disabled={error ? true : false}
|
||||
>
|
||||
{t('upload-file')}
|
||||
</button>
|
||||
@@ -379,15 +446,20 @@ export default function UploadSectionFile({ fileType, allowedExtensions }: Uploa
|
||||
onClick={
|
||||
async () => {
|
||||
if (uploadId) {
|
||||
console.log('checkChunkStatus')
|
||||
const response = await checkChunkStatus(uploadId);
|
||||
console.log(response);
|
||||
await checkChunkStatus(uploadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
dev-test-button
|
||||
</button>
|
||||
{uploadProgress !== 0 && (
|
||||
<div
|
||||
className={`absolute top-5 right-5 font-medium ${isFileUploaded ? "text-green-600" : ""}`}
|
||||
>
|
||||
{uploadProgress}%
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user