React拖拽上传PDF报错:Cannot read properties of undefined (reading 'type')
拖拽PDF文件报错"Cannot read properties of undefined (reading 'type')"的解决办法
我需要实现拖拽文件功能,获取文件名并将文件内容转换为Base64格式。但拖拽上传文档时,执行到if(event.dataTransfer.files[0].type !== "application/pdf")代码行时,出现错误:"Cannot read properties of undefined (reading 'type')"。尝试添加文件存在判断后,代码直接跳过判断退出函数,无法处理文件信息。简化后的代码如下:
import React, {useState} from "react"; import { Box, Button, Typography } from "@material-ui/core"; import data from "./data.json" import UploadFileIcon from "@mui/icons-material/UploadFile"; const Up = ({}) => { const [popUpConfirm, setPopUpConfirm] = useState(false); const [selectedFile, setSelectedFile] = useState(); const [isFilePicked, setIsFilePicked] = useState(false); const [isLoading, setIsLoading] = useState(false); const handleSubmit = (event) => { event.preventDefault(); setPopUpConfirm(true); }; const changeHandler = (event) => { //changeHandler works }; const handleDragOver = (event) => { event.preventDefault(); event.stopPropagation(); }; const handleDrop = (event) => { event.preventDefault(); setIsLoading(true); if(event.dataTransfer.files[0].type !== "application/pdf") { alert("Must be PDF."); return; } if(event.target.files) { setSelectedFile(event.dataTransfer.files[0]); setIsFilePicked(true); data.documentName = event.dataTransfer.files[0].name; getBase64(event.dataTransfer.files[0]).then( doc => data.document = doc ); } setIsLoading(false); }; //extracts the base64 content (removes the metadata at the beginning) and ensures padding is done correctly. function getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { let encoded = reader.result.toString().replace(/^data:(.*,)?/, ''); if ((encoded.length % 4) > 0) { encoded += '='.repeat(4 - (encoded.length % 4)); } resolve(encoded); }; reader.onerror = error => reject(error); }); } return ( <> <form onSubmit={handleSubmit}> <Box onDragOver={handleDragOver} onDrop={handleDrop}> <Box> <Button > {" "} <UploadFileIcon/> Choose a document <input type="file" name="file" onChange={changeHandler} hidden/> </Button> <Typography>Or drop your document here</Typography> {isFilePicked ? <Typography> {selectedFile.name} </Typography> : null} </Box> </Box> <Box> <Button type="submit"> UPLOAD </Button> </Box> </form> <UploadFile open={popUpConfirm} setOpen={setPopUpConfirm} data={data}/> </> ); } export default Up;
问题根源
- 直接访问
event.dataTransfer.files[0]未做存在性校验,当拖拽的不是文件(比如纯文本)或未拖拽任何文件时,files[0]会是undefined,读取type属性自然报错。 - 文件存在判断逻辑错误:
event.target.files是针对文件选择框的逻辑,拖拽场景下应检查event.dataTransfer.files的长度,而非event.target.files,这导致有效文件被错误跳过处理。
修复后的代码
import React, {useState} from "react"; import { Box, Button, Typography } from "@material-ui/core"; import data from "./data.json" import UploadFileIcon from "@mui/icons-material/UploadFile"; const Up = ({}) => { const [popUpConfirm, setPopUpConfirm] = useState(false); const [selectedFile, setSelectedFile] = useState(); const [isFilePicked, setIsFilePicked] = useState(false); const [isLoading, setIsLoading] = useState(false); const handleSubmit = (event) => { event.preventDefault(); setPopUpConfirm(true); }; const changeHandler = (event) => { //changeHandler works }; const handleDragOver = (event) => { event.preventDefault(); event.stopPropagation(); }; const handleDrop = (event) => { event.preventDefault(); setIsLoading(true); // 先判断是否有拖拽文件 const files = event.dataTransfer.files; if (!files || files.length === 0) { alert("请拖拽文件"); setIsLoading(false); return; } const file = files[0]; // 校验是否为PDF if(file.type !== "application/pdf") { alert("必须上传PDF文件"); setIsLoading(false); return; } // 处理文件逻辑 setSelectedFile(file); setIsFilePicked(true); data.documentName = file.name; getBase64(file).then( doc => data.document = doc ); setIsLoading(false); }; //extracts the base64 content (removes the metadata at the beginning) and ensures padding is done correctly. function getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { let encoded = reader.result.toString().replace(/^data:(.*,)?/, ''); if ((encoded.length % 4) > 0) { encoded += '='.repeat(4 - (encoded.length % 4)); } resolve(encoded); }; reader.onerror = error => reject(error); }); } return ( <> <form onSubmit={handleSubmit}> <Box onDragOver={handleDragOver} onDrop={handleDrop}> <Box> <Button > {" "} <UploadFileIcon/> 选择文档 <input type="file" name="file" onChange={changeHandler} hidden/> </Button> <Typography>或拖拽文档到此处</Typography> {isFilePicked ? <Typography> {selectedFile?.name} </Typography> : null} </Box> </Box> <Box> <Button type="submit"> 上传 </Button> </Box> </form> <UploadFile open={popUpConfirm} setOpen={setPopUpConfirm} data={data}/> </> ); } export default Up;
修复要点
- 先获取
event.dataTransfer.files,判断是否存在且长度大于0,避免直接访问files[0]导致的报错。 - 将错误的
event.target.files判断替换为正确的文件存在校验逻辑。 - 渲染文件名时添加可选链
selectedFile?.name,防止组件初始渲染或未选择文件时的报错。 - 所有分支都添加
setIsLoading(false),避免加载状态一直停留。
内容的提问来源于stack exchange,提问作者peips
相关产品推荐
相关产品推荐

