当我提交照片进行拍摄时,我在 AutoDetailing.js 中收到错误。
上传照片或分析图像时出错:响应缺少数据字段。 AutoDetailing.js:37:20 错误详细信息:FirebaseError:响应缺少数据字段。
但我在 Firebase 日志中收到以下内容。
从 OpenAI API 收到标签:图像显示一名年轻人在室内环境中,对着摄像头举起一个绿色罐子,罐子上写着“回收我”。他面带微笑,穿着浅色T恤。背景似乎是中性色调的家居室内装饰。重点是根据罐上可见的文字促进回收。
下面是我的代码。
AutoDetailing.js
import React, { useState } from "react";
import Camera, { FACING_MODES } from 'react-html5-camera-photo';
import 'react-html5-camera-photo/build/css/index.css';
import PropTypes from 'prop-types';
import { useForm } from "react-hook-form";
import { storage, functions } from './firebase'; // Ensure the correct path
import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
import { httpsCallable } from 'firebase/functions';
function AutoCameraApp({ onClose, onPhotoCapture, formData }) {
const handlePhotoCapture = async (dataUri) => {
console.log('Photo captured:', dataUri);
const blob = await fetch(dataUri).then(res => res.blob());
const storageRef = ref(storage, `vehicle-photos/${Date.now()}-vehicle-photo.png`);
try {
const snapshot = await uploadBytes(storageRef, blob);
console.log('Uploaded the file!', snapshot);
const downloadUrl = await getDownloadURL(snapshot.ref);
console.log('File available at', downloadUrl);
const analyzeImage = httpsCallable(functions, 'analyzeImage');
const payload = { imageUrl: downloadUrl };
console.log('Calling analyzeImage with payload:', payload);
const result = await analyzeImage(payload);
console.log('Result from analyzeImage:', result);
if (result.data && result.data.labels) {
onPhotoCapture(downloadUrl, result.data.labels);
} else {
console.error('Response is missing labels field:', result.data);
}
} catch (error) {
console.error('Error uploading photo or analyzing image:', error.message);
console.error('Error details:', error);
}
console.log('Form Data:', formData);
};
return (
<div className="camera-fullscreen">
<div className="camera-header">
Take a picture of your vehicle
</div>
<Camera
onTakePhoto={(dataUri) => { handlePhotoCapture(dataUri); }}
isFullscreen={true}
idealFacingMode={FACING_MODES.ENVIRONMENT}
isMaxResolution={true}
/>
<button onClick={onClose}>Close</button>
</div>
);
AutoCameraApp.propTypes = {
onClose: PropTypes.func.isRequired,
onPhotoCapture: PropTypes.func.isRequired,
formData: PropTypes.object
};
const AutoDetailing = ({ vehicleTypes, pricingMatrix, setCurrentVehicle }) => {
const { register, handleSubmit, watch, formState: { errors }, getValues } = useForm();
const [selectedVehicle, setSelectedVehicle] = useState(null);
const [showCamera, setShowCamera] = useState(false);
const [photoData, setPhotoData] = useState(null);
const [analysisResult, setAnalysisResult] = useState(null);
const [isFormVisible, setIsFormVisible] = useState(false);
const onSubmit = data => {
console.log('Form submitted:', data);
// Handle form submission here
};
const handleVehicleSelect = (vehicleType) => {
setSelectedVehicle(vehicleType);
console.log('Vehicle selected:', vehicleType);
};
const handlePackageSelect = (packageType) => {
setCurrentVehicle(selectedVehicle);
setShowCamera(false);
setIsFormVisible(true);
console.log('Package selected:', packageType);
};
const handleCloseCamera = () => {
setShowCamera(false);
setIsFormVisible(false);
console.log('Camera closed');
};
const handlePhotoCapture = (downloadUrl, analysis) => {
setPhotoData(downloadUrl);
setAnalysisResult(analysis);
setShowCamera(false);
setIsFormVisible(false);
console.log('Photo captured and analyzed:', { downloadUrl, analysis });
};
const watchFields = watch(["firstName", "lastName", "threeAvailDatesTimes", "address"]);
const isFormComplete = watchFields.every(field => field && field.trim() !== "");
const formData = getValues();
return (
<div className="service-detail-wrapper">
<p className="service-description">
We're deeply committed to the art of automotive detailing, ensuring no detail is overlooked in our meticulous process. Our approach combines safe solutions with expert care to maintain the highest quality standards, preserving your vehicle's finish impeccably. Did you know that most swirls on vehicles stem from improper hand washing or automatic car washes? We offer various detailing packages tailored to your needs, with our standard package recommended biannually and the full package annually. You can also customize your package to suit your preferences. Rest assured, our dedication to customer satisfaction means we'll promptly address any concerns to ensure your experience with us is exceptional.
</p>
{isFormVisible ? (
<div className="booking-overlay">
<form className="fullscreen-form" onSubmit={handleSubmit(onSubmit)}>
<h3>Fill out the following form</h3>
<div className="input-fields">
<input type="text" placeholder="First Name" {...register("firstName", { required: true })} />
{errors.firstName && <span>This field is required</span>}
<input type="text" placeholder="Last Name" {...register("lastName", { required: true })} />
{errors.lastName && <span>This field is required</span>}
<input type="tel" placeholder="Phone" {...register("phone", { required: true })} />
{errors.phone && <span>This field is required</span>}
<input type="email" placeholder="Email" {...register("email", { required: true })} />
{errors.email && <span>This field is required</span>}
<input type="text" placeholder="3 Available Dates / Times" {...register("threeAvailDatesTimes", { required: true })} />
{errors.threeAvailDatesTimes && <span>This field is required</span>}
<input type="text" placeholder="Address" {...register("address", { required: true })} />
{errors.address && <span>This field is required</span>}
<button type="button" onClick={() => setShowCamera(true)} className="view-packages" disabled={!isFormComplete}>Proceed to vehicle capture</button>
</div>
</form>
</div>
) : (
<div>
<h3>Select a Vehicle</h3>
<div className="vehicle-buttons">
{vehicleTypes.map((vehicleType, index) => (
<button key={index} className="view-packages" onClick={() => handleVehicleSelect(vehicleType)}>
{vehicleType}
</button>
))}
</div>
{selectedVehicle && pricingMatrix[selectedVehicle] && (
<div className="pricing-matrix">
<h3>Select a Package</h3>
<div className="package">
<h4>Basic Package</h4>
<p className="service-description">{pricingMatrix[selectedVehicle].basicDescription}</p>
<p className="service-cost">Price: ${pricingMatrix[selectedVehicle].basic}</p>
<button onClick={() => handlePackageSelect('basic')} className="view-packages">Book Now</button>
</div>
<div className="package">
<h4>Standard Package</h4>
<p className="service-description">{pricingMatrix[selectedVehicle].standardDescription}</p>
<p className="service-cost">Price: ${pricingMatrix[selectedVehicle].standard}</p>
<button onClick={() => handlePackageSelect('standard')} className="view-packages">Book Now</button>
</div>
<div className="package">
<h4>Full Package</h4>
<p className="service-description">{pricingMatrix[selectedVehicle].fullDescription}</p>
<p className="service-cost">Price: ${pricingMatrix[selectedVehicle].full}</p>
<button onClick={() => handlePackageSelect('full')} className="view-packages">Book Now</button>
</div>
</div>
)}
</div>
)}
{showCamera && <AutoCameraApp onClose={handleCloseCamera} onPhotoCapture={handlePhotoCapture} formData={formData} />}
<div className="faqs">
<h3 className="faq-header">Frequently Asked Questions</h3>
<div className="faq">
<h4>How do you ensure safe detailing of exterior surfaces?</h4>
<p>To ensure safe detailing of exterior surfaces, we use a three-bucket method along with new towels for each detail. Our wheel brushes are soft, cleaned after each use, and inspected for damage. The method includes soap, rinse, and wheel buckets, each with a grit guard to catch contaminants. We then go over the vehicle's panels after rinsing them off.</p>
</div>
<div className="faq">
<h4>How can I trust the interior detailing to be immaculate?</h4>
<p>We understand the importance of interior detailing and use specialized tools such as steam cleaners, heated carpet extractors, ozone generators, specialized chemicals, and detailing brushes. Rest assured, we have all the necessary equipment and expertise to deliver exceptional interior detailing.</p>
</div>
<div className="faq">
<h4>How often should I detail my car to keep it looking new?</h4>
<p>The frequency depends on your preferences and budget. For minimal effort and year-round pristine condition, we recommend a ceramic coating package. Otherwise, we suggest a full detail annually, a standard detail biannually, and a basic detail as needed. Note that foregoing ceramic coating may result in higher long-term costs.</p>
</div>
</div>
</div>
);
};
export default AutoDetailing;
index.js
const functions = require("firebase-functions");
const cors = require("cors")({ origin: true });
const admin = require("firebase-admin");
const OpenAI = require("openai");
admin.initializeApp();
const openai = new OpenAI({
apiKey: 'REDACTED', // Replace this with your actual OpenAI API key
});
exports.analyzeImage = functions.https.onRequest((req, res) => {
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (req.method === "OPTIONS") {
res.status(204).send("");
return;
}
cors(req, res, async () => {
console.log("Request received:", req.body);
const imageUrl = req.body.imageUrl || (req.body.data && req.body.data.imageUrl);
console.log("Received imageUrl:", imageUrl);
if (!imageUrl) {
console.error("Missing imageUrl");
return res.status(400).send("Missing imageUrl");
}
try {
const messages = [
{
role: "user",
content: [
{ type: "text", text: "What’s in this image?" },
{ type: "image_url", image_url: { url: imageUrl } },
],
},
];
console.log("Messages payload:", JSON.stringify(messages, null, 2));
const openaiResponse = await openai.chat.completions.create({
model: "gpt-4-turbo", // Adjust the model as needed
messages: messages,
max_tokens: 300,
});
console.log("OpenAI API response:", JSON.stringify(openaiResponse, null, 2));
if (openaiResponse.choices && openaiResponse.choices.length > 0) {
const labels = openaiResponse.choices[0].message.content;
console.log("Received labels from OpenAI API:", labels);
res.status(200).json({ labels }); // Ensure to send a JSON response
} else {
throw new Error("Invalid response structure");
}
} catch (error) {
console.error("Error analyzing image:", error.message);
if (error.response) {
console.error("Error response data:", JSON.stringify(error.response.data, null, 2));
}
res.status(500).send("Internal Server Error");
}
});
});
firebase.js
const firebase = require('firebase/app');
require('firebase/firestore');
require('firebase/storage');
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
const storage = firebase.storage();
module.exports = { db, storage };
我尝试使用 base64 来传递图像,但失败了,我在 Firebase 中收到带有图像 url 的响应,但在控制台中却没有。
你解决这个问题了吗?你比我做得更远,我无法让我的助手做出回应。所以,在 firebase 中进行图像分析真是太棒了!我认为您所需要做的就是让您的代码将分析从 Firebase 检索到您的用户界面。你解决了吗?