Feature/support xlsx (#311)

This commit is contained in:
crazywoola
2023-06-08 15:23:38 +08:00
committed by GitHub
parent dbe0c43515
commit df26f82536
12 changed files with 594 additions and 488 deletions

View File

@@ -1,19 +1,18 @@
'use client'
import React, { useState, useRef, useEffect, useCallback } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { File as FileEntity } from '@/models/datasets'
import { useContext } from 'use-context-selector'
import cn from 'classnames'
import s from './index.module.css'
import type { File as FileEntity } from '@/models/datasets'
import { ToastContext } from '@/app/components/base/toast'
import Button from '@/app/components/base/button'
import { upload } from '@/service/base'
import cn from 'classnames'
import s from './index.module.css'
type IFileUploaderProps = {
file?: FileEntity;
onFileUpdate: (file?: FileEntity) => void;
file?: FileEntity
onFileUpdate: (file?: FileEntity) => void
}
const ACCEPTS = [
@@ -23,9 +22,12 @@ const ACCEPTS = [
'.md',
'.markdown',
'.txt',
'.xls',
'.xlsx',
'.csv',
]
const MAX_SIZE = 15 * 1024 *1024
const MAX_SIZE = 15 * 1024 * 1024
const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
const { t } = useTranslation()
@@ -39,6 +41,84 @@ const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
const [uploading, setUploading] = useState(false)
const [percent, setPercent] = useState(0)
// utils
const getFileType = (currentFile: File) => {
if (!currentFile)
return ''
const arr = currentFile.name.split('.')
return arr[arr.length - 1]
}
const getFileName = (name: string) => {
const arr = name.split('.')
return arr.slice(0, -1).join()
}
const getFileSize = (size: number) => {
if (size / 1024 < 10)
return `${(size / 1024).toFixed(2)}KB`
return `${(size / 1024 / 1024).toFixed(2)}MB`
}
const isValid = (file: File) => {
const { size } = file
const ext = `.${getFileType(file)}`
const isValidType = ACCEPTS.includes(ext)
if (!isValidType)
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.typeError') })
const isValidSize = size <= MAX_SIZE
if (!isValidSize)
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.size') })
return isValidType && isValidSize
}
const onProgress = useCallback((e: ProgressEvent) => {
if (e.lengthComputable) {
const percent = Math.floor(e.loaded / e.total * 100)
setPercent(percent)
}
}, [setPercent])
const abort = () => {
const currentXHR = uploadPromise.current
currentXHR.abort()
}
const fileUpload = async (file?: File) => {
if (!file)
return
if (!isValid(file))
return
setCurrentFile(file)
setUploading(true)
const formData = new FormData()
formData.append('file', file)
// store for abort
const currentXHR = new XMLHttpRequest()
uploadPromise.current = currentXHR
try {
const result = await upload({
xhr: currentXHR,
data: formData,
onprogress: onProgress,
}) as FileEntity
onFileUpdate(result)
setUploading(false)
}
catch (xhr: any) {
setUploading(false)
// abort handle
if (xhr.readyState === 0 && xhr.status === 0) {
if (fileUploader.current)
fileUploader.current.value = ''
setCurrentFile(undefined)
return
}
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.failed') })
}
}
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
@@ -57,27 +137,26 @@ const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
e.preventDefault()
e.stopPropagation()
setDragging(false)
if (!e.dataTransfer) {
if (!e.dataTransfer)
return
}
const files = [...e.dataTransfer.files]
if (files.length > 1) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.count') })
return;
return
}
onFileUpdate()
fileUpload(files[0])
}
const selectHandle = () => {
if (fileUploader.current) {
fileUploader.current.click();
}
if (fileUploader.current)
fileUploader.current.click()
}
const removeFile = () => {
if (fileUploader.current) {
if (fileUploader.current)
fileUploader.current.value = ''
}
setCurrentFile(undefined)
onFileUpdate()
}
@@ -86,96 +165,17 @@ const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
onFileUpdate()
fileUpload(currentFile)
}
const fileUpload = async (file?: File) => {
if (!file) {
return
}
if (!isValid(file)) {
return
}
setCurrentFile(file)
setUploading(true)
const formData = new FormData()
formData.append('file', file)
// store for abort
const currentXHR = new XMLHttpRequest()
uploadPromise.current = currentXHR
try {
const result = await upload({
xhr: currentXHR,
data: formData,
onprogress: onProgress,
}) as FileEntity;
onFileUpdate(result)
setUploading(false)
}
catch (xhr: any) {
setUploading(false)
// abort handle
if (xhr.readyState === 0 && xhr.status === 0) {
if (fileUploader.current) {
fileUploader.current.value = ''
}
setCurrentFile(undefined)
return
}
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.failed') })
return
}
}
const onProgress = useCallback((e: ProgressEvent) => {
if (e.lengthComputable) {
const percent = Math.floor(e.loaded / e.total * 100)
setPercent(percent)
}
}, [setPercent])
const abort = () => {
const currentXHR = uploadPromise.current
currentXHR.abort();
}
// utils
const getFileType = (currentFile: File) => {
if (!currentFile) {
return ''
}
const arr = currentFile.name.split('.')
return arr[arr.length-1]
}
const getFileName = (name: string) => {
const arr = name.split('.')
return arr.slice(0, -1).join()
}
const getFileSize = (size: number) => {
if (size / 1024 < 10) {
return `${(size / 1024).toFixed(2)}KB`
}
return `${(size / 1024 / 1024).toFixed(2)}MB`
}
const isValid = (file: File) => {
const { size } = file
const ext = `.${getFileType(file)}`
const isValidType = ACCEPTS.includes(ext)
if (!isValidType) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.typeError') })
}
const isValidSize = size <= MAX_SIZE;
if (!isValidSize) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.size') })
}
return isValidType && isValidSize;
}
useEffect(() => {
dropRef.current?.addEventListener('dragenter', handleDragEnter);
dropRef.current?.addEventListener('dragover', handleDragOver);
dropRef.current?.addEventListener('dragleave', handleDragLeave);
dropRef.current?.addEventListener('drop', handleDrop);
dropRef.current?.addEventListener('dragenter', handleDragEnter)
dropRef.current?.addEventListener('dragover', handleDragOver)
dropRef.current?.addEventListener('dragleave', handleDragLeave)
dropRef.current?.addEventListener('drop', handleDrop)
return () => {
dropRef.current?.removeEventListener('dragenter', handleDragEnter);
dropRef.current?.removeEventListener('dragover', handleDragOver);
dropRef.current?.removeEventListener('dragleave', handleDragLeave);
dropRef.current?.removeEventListener('drop', handleDrop);
dropRef.current?.removeEventListener('dragenter', handleDragEnter)
dropRef.current?.removeEventListener('dragover', handleDragOver)
dropRef.current?.removeEventListener('dragleave', handleDragLeave)
dropRef.current?.removeEventListener('drop', handleDrop)
}
}, [])
@@ -202,7 +202,7 @@ const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
{currentFile && (
<div className={cn(s.file, uploading && s.uploading)}>
{uploading && (
<div className={s.progressbar} style={{ width: `${percent}%`}}/>
<div className={s.progressbar} style={{ width: `${percent}%` }}/>
)}
<div className={cn(s.fileIcon, s[getFileType(currentFile)])}/>
<div className={s.fileInfo}>
@@ -264,4 +264,4 @@ const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
)
}
export default FileUploader;
export default FileUploader