如何获取裁剪后的图片文件以上传到服务器(React+react-easy-crop)
解决方案:获取可上传的裁剪后图片文件
要解决无法获取可直接上传至服务器的裁剪后图片文件的问题,需要将当前生成的Base64字符串转换为File对象,以下是具体修改步骤:
1. 修改图片裁剪函数,生成Blob对象
将原有的cropImage和cropImage64函数修改为返回Blob(二进制大对象),这是生成File的基础:
function image64ToImage(base64: any) { return new Promise((resolve, reject) => { const img = new Image(); img.src = base64; img.onload = () => resolve(img); img.onerror = () => reject(new Error("图片加载失败")); }); } function cropImage(image: any, x: any, y: any, newWidth: any, newHeight: any) { return new Promise<Blob | null>((resolve) => { const canvas = document.createElement("canvas"); canvas.width = newWidth; canvas.height = newHeight; const ctx = canvas.getContext("2d"); if (!ctx) { resolve(null); return; } ctx.drawImage(image, x, y, newWidth, newHeight, 0, 0, newWidth, newHeight); // 将画布内容转换为JPEG格式的Blob canvas.toBlob((blob) => resolve(blob), "image/jpeg", 0.9); }); } async function cropImage64(base64: any, x: any, y: any, newWidth: any, newHeight: any) { try { const img = await image64ToImage(base64); return cropImage(img, x, y, newWidth, newHeight); } catch (error) { console.error("裁剪图片出错:", error); return null; } }
2. 更新ImageCroper组件,生成File对象
修改ImageCroper组件,接收原文件名称并将Blob转换为可上传的File对象:
function ImageCroper({ image, fileName, setCropedImage }: { image: string, fileName: string | null, setCropedImage: (file: File) => void }) { const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState<number>(1); const [coord, setCoord] = useState<any>(null); const onCropComplete = useCallback((croppedArea: any, croppedAreaPixels: any) => { setCoord(croppedAreaPixels); }, []); const cutImage = async () => { if (!coord || !fileName) return; const blob = await cropImage64(image, coord.x, coord.y, coord.width, coord.height); if (!blob) return; // 将Blob转换为File对象,保留原文件名称前缀 const croppedFile = new File([blob], `cropped-${fileName}`, { type: blob.type }); setCropedImage(croppedFile); }; return ( <div className="cropper"> <div className="cropper-container"> <Cropper image={image} crop={crop} zoom={zoom} cropShape="round" showGrid={false} aspect={1} onCropChange={setCrop} onCropComplete={onCropComplete} onZoomChange={setZoom} /> </div> <div className="cropper-controls"> <input className="cropper-zoom-range" type="range" value={zoom} min={1} max={3} step={0.01} aria-labelledby="Zoom" onChange={(e) => setZoom(+e.target.value)} /> <CustomButton variant="red" icon="none" w="150px" onClick={cutImage} text="SIMPAN" /> </div> </div> ); } ImageCroper.propTypes = { image: PropTypes.string.isRequired, fileName: PropTypes.string, setCropedImage: PropTypes.func.isRequired };
3. 调整ImageLoader组件,处理预览与回调
在ImageLoader中添加原文件名称的状态管理,并将File对象转换为Base64用于预览,同时将File传递给父组件:
function ImageLoader({ image, callback, size }: { image: string, callback: (file: File) => void, size: number }) { const [file, setFile] = useState<File | null>(null); const [result, setResult] = useState<string | null>(null); const [crop, toogleCrop] = useReducer((currCrop) => !currCrop, false); const [imageState, setImage] = useState<string>(image); const [fileName, setFileName] = useState<string | null>(null); useEffect(() => { if (!file) return; setFileName(file.name); const reader = new FileReader(); reader.onloadend = () => { setResult(reader.result as string); toogleCrop(); }; reader.readAsDataURL(file); }, [file]); return ( <> <label className="logo" style={{ width: size, height: size, backgroundSize: size, backgroundImage: `url(${imageState})`, }} > <input type="file" accept="image/*" style={{ display: "none" }} onChange={(e) => { e.preventDefault(); const selectedFile = e.target.files?.[0]; if (selectedFile) setFile(selectedFile); }} /> </label> {crop && result && ( <ImageCroper image={result} fileName={fileName} setCropedImage={(croppedFile) => { // 将File转换为Base64用于预览 const reader = new FileReader(); reader.onloadend = () => setImage(reader.result as string); reader.readAsDataURL(croppedFile); toogleCrop(); callback(croppedFile); }} /> )} </> ); } ImageLoader.propTypes = { image: PropTypes.string.isRequired, callback: PropTypes.func.isRequired, size: PropTypes.number.isRequired, };
4. 更新Setting组件,接收File对象并上传
修改Setting组件中的状态和提交逻辑,直接使用File对象:
const Setting = () => { // ...其他状态 const [newPicture, setNewPicture] = useState<File | null>(null); // ...其他函数 const handleSubmit = async () => { const formData = new FormData(); formData.append('name', name); formData.append('phoneNumber', phoneNumber); if (newPicture) { // 直接将File对象添加到FormData formData.append('profilePhoto', newPicture); } formData.append('subscribeNewsletter', String(isSubscribe)); // ...提交逻辑 }; return ( // ...其他JSX <ImageLoader size={155} image={memberData?.photoUrl || 'https://www.seekpng.com/png/detail/966-9665317_placeholder-image-person-jpg.png'} callback={(imageFile) => setNewPicture(imageFile)} /> // ...其他JSX ); };
核心原理说明
- Blob转File:通过
canvas.toBlob()生成图片的二进制Blob,再用new File()将Blob包装为符合上传要求的File对象 - 预览与上传分离:将File转换为Base64用于页面预览,同时保留原始File对象用于服务器上传
- FormData兼容:File对象可以直接添加到FormData中,符合后端接口的文件接收规范
内容的提问来源于stack exchange,提问作者Estherak23
相关产品推荐
相关产品推荐

