file upload update

This commit is contained in:
smanylov
2025-12-24 21:36:59 +07:00
parent fe77b4ad36
commit 87b8d68f41
9 changed files with 156 additions and 74 deletions
@@ -1,16 +1,16 @@
import ProtectionSummary from '@/app/ui/marking-page/protection-summary';
import TestSection from '@/app/ui/marking-page/test-section';
import FilesTable from '@/app/ui/dashboard/files-table';
import {useTranslations} from 'next-intl';
import PageTitle from '@/app/ui/page-title';
const ALLOWED_EXTENSIONS: string[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
const FILE_TYPE = "VIDEO"
const MAX_SIZE = 1048576000;
export default function Page() {
const t = useTranslations('Global');
return (
<div>
<div className="page-title">
<h1>{t('audio-protection')}</h1>
</div>
<PageTitle title="audio-protection" />
<ProtectionSummary />
{/* <TestSection /> */}
<FilesTable />
@@ -2,21 +2,22 @@ import ProtectionSummary from '@/app/ui/marking-page/protection-summary';
import PageTitle from '@/app/ui/page-title';
import UploadSectionFile from '@/app/components/upload-section-file';
import FilesTable from '@/app/ui/dashboard/files-table';
import { FileExtension } from '@/app/actions/definitions';
import { getAllowedFilesExtensions } from '@/app/actions/file-upload';
const ALLOWED_EXTENSIONS: FileExtension[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
const ALLOWED_EXTENSIONS: string[] = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];
const FILE_TYPE = "IMAGE"
const MAX_SIZE = 1048576000;
export default async function Page() {
/* const allowedFilesExtensions = await getAllowedFilesExtensions(FILE_TYPE);
console.log(allowedFilesExtensions); */
/* const allowedFilesExtensions = await getAllowedFilesExtensions(FILE_TYPE);
console.log(allowedFilesExtensions);
console.log(allowedFilesExtensions); */
return (
<div>
<PageTitle title="image-protection" />
<ProtectionSummary />
<UploadSectionFile fileType={FILE_TYPE} allowedExtensions={ALLOWED_EXTENSIONS} />
<UploadSectionFile fileType={FILE_TYPE} allowedExtensions={ALLOWED_EXTENSIONS} maxFileSize={MAX_SIZE} />
{/* <TestSection /> */}
<FilesTable />
{/* <ProtectedFilesTable /> */}
@@ -1,20 +1,20 @@
import ProtectionSummary from '@/app/ui/marking-page/protection-summary';
import TestSection from '@/app/ui/marking-page/test-section';
import UploadSectionVideo from '@/app/ui/marking-page/upload-section-video';
import FilesTable from '@/app/ui/dashboard/files-table';
import {useTranslations} from 'next-intl';
import PageTitle from '@/app/ui/page-title';
import UploadSectionFile from '@/app/components/upload-section-file';
const ALLOWED_EXTENSIONS: string[] = ['mp4', 'avi', 'webm', 'mkv', 'flv', 'wmv', 'mov'];
const FILE_TYPE = "VIDEO"
const MAX_SIZE = 1048576000;
export default function Page() {
const t = useTranslations('Global');
return (
<div>
<div className="page-title">
<h1>{t('video-protection')}</h1>
</div>
<PageTitle title="video-protection" />
<ProtectionSummary />
<UploadSectionVideo />
<UploadSectionFile fileType={FILE_TYPE} allowedExtensions={ALLOWED_EXTENSIONS} maxFileSize={MAX_SIZE} />
{/* <TestSection /> */}
<FilesTable />
</div>
-2
View File
@@ -78,8 +78,6 @@ export const loginFormSchema = z
export const localDevelopmentUrl = 'http://localhost';
export type FileExtension = 'jpg' | 'jpeg' | 'png' | 'gif' | 'bmp';
export const testUserData = {
fullName: 'test',
email: 'test@mail.com',
+121 -49
View File
@@ -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>
)}
+8
View File
@@ -517,6 +517,14 @@
min-width: 160px;
transform: translateY(0px);
box-shadow: none;
&.disabled {
opacity: 0.5;
&:hover {
transform: none;
}
}
}
.btn-cancel {
@@ -1,4 +1,5 @@
'use client';
/* removed */
import { useState, useRef } from 'react';
+4 -3
View File
@@ -17,7 +17,7 @@
"checks": "Checks",
"check": "Checks",
"videos": "Videos",
"video": "vides",
"video": "videos",
"audios": "Audio",
"audio": "audio",
"images": "Images",
@@ -126,8 +126,9 @@
"or": "or",
"drag-the-file-here": "Drag the file here",
"select-files-to-protect": "Select files to protect",
"download-for-protection": "Download {fileType} for protection",
"image": "image"
"upload-for-protection": "Upload {fileType} for protection",
"image": "image",
"file-has-no-extension": "File has no extension"
},
"Login-register-form": {
"and": "and",
+3 -2
View File
@@ -126,8 +126,9 @@
"or": "или",
"drag-the-file-here": "Перетащите файл сюда",
"select-files-to-protect": "Выбрать файлы для защиты",
"download-for-protection": "Загрузить {fileType} для защиты",
"image": "изображений"
"upload-for-protection": "Загрузить {fileType} для защиты",
"image": "изображений",
"file-has-no-extension": "Файл не имеет расширения"
},
"Login-register-form": {
"and": "и",