React上传Firebase前图片Resize报错:resize is not a function
客户端图片 resize 后上传 Firebase Storage 报错 'resize is not a function' 排查方案
问题概述
尝试在React客户端调整图片大小后上传至Firebase Storage,但控制台报错resize is not a function。已确认依赖配置正确,核心问题集中在图片resize环节,相关代码如下:
原React组件代码
import React, { useState, useEffect } from "react"; import Resizer from "react-image-file-resizer"; import { uploadImagesToFirebaseStorage } from "../firebase_handlers/firebaseStorageHandler"; const ImageFirestoreUplaoderComponent = () => { const [selectedFiles, setSelectedFiles] = useState([]); const [resizedImages, setResizedImages] = useState([]); const [thumbnailImages, setThumbnailImages] = useState([]); const [processingComplete, setProcessingComplete] = useState(false); // Function to encode a file to base64 const encodeToBase64 = (file) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { resolve(reader.result); }; reader.onerror = (error) => { reject(error); }; reader.readAsDataURL(file); }); // Function to encode an array of files to base64 const encodeFilesToBase64 = async (files) => { try { const encodedImages = await Promise.all( files.map((file) => encodeToBase64(file)) ); return encodedImages; } catch (error) { console.error("Error encoding files to base64:", error); return []; } }; // Resizing and creating thumbnail miniature const resizeImages = async (base64Images) => { try { const resizedImages = []; const thumbnailImages = []; await Promise.all( base64Images.map(async (base64) => { try { // Resize image to a maximum width of 800px const resizedDataUrl = await Resizer.resize(base64, 800, 600); resizedImages.push({ dataUrl: resizedDataUrl, file: null, // You can set the original file here if needed for later use }); // Create a thumbnail miniature with a maximum width of 200px const thumbnailDataUrl = await Resizer.resize(base64, 200, 150); thumbnailImages.push({ dataUrl: thumbnailDataUrl, file: null, }); } catch (error) { console.error("Error resizing image:", error); } }) ); return { resizedImages, thumbnailImages }; } catch (error) { console.error("Error resizing images:", error); return { resizedImages: [], thumbnailImages: [] }; } }; useEffect(() => { if (resizedImages.length > 0 && processingComplete) { // Call the upload function only if resizedImages contains valid data handleUploadImages(); // Reset states after clicking upload setSelectedFiles([]); setResizedImages([]); setThumbnailImages([]); setProcessingComplete(false); } }, [resizedImages, processingComplete]); const handleImageChange = async (e) => { const files = e.target.files; await new Promise((resolve) => { setSelectedFiles((prevSelectedFiles) => [...prevSelectedFiles, ...files]); resolve(); }); const processImages = async () => { const base64Images = await encodeFilesToBase64(selectedFiles); const { resizedImages, thumbnailImages } = await resizeImages( base64Images ); setResizedImages(resizedImages); setThumbnailImages(thumbnailImages); setProcessingComplete(true); }; if (selectedFiles.length > 0) { await processImages(); } }; const handleUploadImages = async () => { await uploadImagesToFirebaseStorage(resizedImages, thumbnailImages); }; return ( <div> <input type="file" multiple onChange={handleImageChange} /> <button onClick={handleUploadImages}> Upload Resized Images and Thumbnail Miniatures to Firebase </button> </div> ); }; export default ImageFirestoreUplaoderComponent;
原Firebase上传代码
import { storage } from "../firebase_setup/firebase"; import { ref, uploadString } from "firebase/storage"; const storageRef = ref(storage, "some-child"); const uploadImagesToFirebaseStorage = async ( resizedImages, thumbnailImages ) => { try { const allImages = [...resizedImages, ...thumbnailImages]; const uploadPromises = allImages.map(async (imageData, index) => { const { dataUrl } = imageData; // Convert dataUrl to base64 format (remove "data:image/jpeg;base64," or similar prefix) const base64Data = dataUrl.replace(/^data:image\/[a-z]+;base64,/, ""); // Use uploadString to upload the base64 data to Firebase Storage const uploadTask = uploadString(storageRef, base64Data, "base64"); console.log(imageData); return new Promise((resolve, reject) => { uploadTask.then( (snapshot) => { console.log(`Image ${index + 1} uploaded successfully!`); resolve(snapshot); }, (error) => reject(error) ); }); }); await Promise.all(uploadPromises); console.log("All images uploaded successfully!"); } catch (error) { console.error("Error uploading images to Firebase Storage:", error); } }; export { uploadImagesToFirebaseStorage };
错误原因分析
- react-image-file-resizer API 使用错误:该库的resize方法并非直接接收base64字符串和宽高参数,而是需要传入配置对象,且默认是回调式调用,不能直接用
await。 - React State 异步更新问题:
handleImageChange中调用setSelectedFiles后立即使用selectedFiles,此时state尚未更新,导致后续处理拿到的是空数组或旧数据。 - Firebase存储引用重复:固定的
storageRef会导致所有上传的图片覆盖同一位置,无法保存多个文件。
修复方案及代码调整
1. 封装react-image-file-resizer的Promise版本
将回调式的resize方法封装为Promise,适配async/await语法:
const resizeImage = (fileOrBase64, maxWidth, maxHeight) => { return new Promise((resolve, reject) => { Resizer.imageFileResizer( fileOrBase64, maxWidth, maxHeight, "JPEG", 80, 0, (uri) => resolve(uri), "base64", maxWidth, maxHeight ); }); };
2. 修正React组件中的resize逻辑和State处理
修改resizeImages和handleImageChange函数,解决API调用和异步State问题:
import React, { useState, useEffect } from "react"; import Resizer from "react-image-file-resizer"; import { uploadImagesToFirebaseStorage } from "../firebase_handlers/firebaseStorageHandler"; const ImageFirestoreUplaoderComponent = () => { const [selectedFiles, setSelectedFiles] = useState([]); const [resizedImages, setResizedImages] = useState([]); const [thumbnailImages, setThumbnailImages] = useState([]); const [processingComplete, setProcessingComplete] = useState(false); // 封装resize为Promise const resizeImage = (fileOrBase64, maxWidth, maxHeight) => { return new Promise((resolve, reject) => { Resizer.imageFileResizer( fileOrBase64, maxWidth, maxHeight, "JPEG", 80, 0, (uri) => resolve(uri), "base64", maxWidth, maxHeight ); }); }; // Resizing and creating thumbnail miniature const resizeImages = async (files) => { try { const resizedImages = []; const thumbnailImages = []; await Promise.all( files.map(async (file) => { try { // 直接传入File对象,无需先转base64,库支持直接处理File const resizedDataUrl = await resizeImage(file, 800, 600); resizedImages.push({ dataUrl: resizedDataUrl, file: file, }); const thumbnailDataUrl = await resizeImage(file, 200, 150); thumbnailImages.push({ dataUrl: thumbnailDataUrl, file: file, }); } catch (error) { console.error("Error resizing image:", error); } }) ); return { resizedImages, thumbnailImages }; } catch (error) { console.error("Error resizing images:", error); return { resizedImages: [], thumbnailImages: [] }; } }; useEffect(() => { if (resizedImages.length > 0 && processingComplete) { handleUploadImages(); setSelectedFiles([]); setResizedImages([]); setThumbnailImages([]); setProcessingComplete(false); } }, [resizedImages, processingComplete]); const handleImageChange = async (e) => { const files = Array.from(e.target.files); setSelectedFiles(prev => [...prev, ...files]); // 直接使用刚获取的files,避免state异步问题 const { resizedImages, thumbnailImages } = await resizeImages(files); setResizedImages(prev => [...prev, ...resizedImages]); setThumbnailImages(prev => [...prev, ...thumbnailImages]); setProcessingComplete(true); }; const handleUploadImages = async () => { await uploadImagesToFirebaseStorage(resizedImages, thumbnailImages); }; return ( <div> <input type="file" multiple onChange={handleImageChange} /> <button onClick={handleUploadImages}> Upload Resized Images and Thumbnail Miniatures to Firebase </button> </div> ); }; export default ImageFirestoreUplaoderComponent;
3. 修正Firebase上传代码的存储引用
为每个图片生成独立的存储路径,避免覆盖:
import { storage } from "../firebase_setup/firebase"; import { ref, uploadString } from "firebase/storage"; const uploadImagesToFirebaseStorage = async ( resizedImages, thumbnailImages ) => { try { const allImages = [...resizedImages, ...thumbnailImages]; const uploadPromises = allImages.map(async (imageData, index) => { const { dataUrl, file } = imageData; const base64Data = dataUrl.replace(/^data:image\/[a-z]+;base64,/, ""); // 生成唯一的存储路径,用文件名+时间戳避免重复 const fileName = `${Date.now()}_${file.name}`; const storageRef = ref(storage, `images/${fileName}`); const snapshot = await uploadString(storageRef, base64Data, "base64"); console.log(`Image ${index + 1} uploaded successfully!`); return snapshot; }); await Promise.all(uploadPromises); console.log("All images uploaded successfully!"); } catch (error) { console.error("Error uploading images to Firebase Storage:", error); } }; export { uploadImagesToFirebaseStorage };
关键修复点说明
- 直接使用
File对象传入resize方法,省去base64转换步骤,提升性能 - 封装resize为Promise,适配async/await语法
- 处理State异步更新问题,避免使用未更新的state值
- 为每个上传文件生成唯一存储路径,防止文件覆盖
内容的提问来源于stack exchange,提问作者Jacor
相关产品推荐
相关产品推荐

