1

I can't figure out why I can't use express and multer in my code above. When I upload my file, all I get in the log is an empty array. I've spent several hours modifying it, I just don't get it!

I've seen that several people have reported errors or bugs between Express and multer, but I still can't solve my problem. If you have any ideas, I'd be grateful!

If you have any ideas, here's my code, thank you!

const functions = require('firebase-functions');
const express = require('express');
const multer = require('multer');
const Replicate = require('replicate');
const cors = require('cors');

const app = express();

// Configuration de Multer avec gestion d'erreurs
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 }, // Limite à 10MB
}).single('image');

app.use(cors({ origin: true }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const replicate = new Replicate({
  auth: functions.config().replicate.api_token,
});

const styleDescriptions = {
  'Gamer': `Upgrade this setup to create a gaming setup, with aesthetic rgb lights...`,
  'Aesthetic': `Transform this setup into a minimalist and visually pleasing design...`,
  'RGB Free': `Enhance this setup to create a professional and sophisticated environment...`
};

app.post('/generate', (req, res) => {
  console.log('Received generate request');
  
  upload(req, res, async function(err) {
    console.log('On arrive ici');
    if (err) {
      console.error('Upload error:', err);
      return res.status(400).json({ error: 'File upload error', details: err.message });
    }

    try {
      const { file } = req;
      const { style } = req.body;

      console.log('File uploaded successfully');
      console.log('File details:', file ? { originalname: file.originalname, size: file.size } : 'No file');
      console.log('Selected style:', style);

      console.log('File details:', file ? { originalname: file.originalname, size: file.size } : 'No file');
      console.log('Selected style:', style);

      if (!file) {  
        return res.status(400).json({ error: 'No image file provided' });
      }

      const styleDescription = styleDescriptions[style] || '';

      console.log('Preparing input for Replicate API');
      const input = {
        image: `data:${file.mimetype};base64,${file.buffer.toString('base64')}`,
        prompt: styleDescription,
        guidance: 3.5,
        num_outputs: 1,
        aspect_ratio: "1:1",
        output_format: "webp",
        output_quality: 80,
        prompt_strength: 0.80,
        num_inference_steps: 28
      };

      console.log('Sending request to Replicate API...');
      const output = await replicate.run(
        "black-forest-labs/flux-dev",
        { input }
      );

      console.log('Replicate API response received');
      res.json({ output: output[0] });
    } catch (error) {
      console.error('Error in generate function:', error);
      res.status(500).json({ error: 'An error occurred while generating the image', details: error.message });
    }
  });
});
// Export the Express app as a Firebase Cloud Function
exports.api = functions.https.onRequest(app);

And here's the client side code where the user can upload the image, and then click on generate :

export const GenerateNew = () => {
  const [image, setImage] = useState(null);
  const [style, setStyle] = useState('Gamer');
  const [loading, setLoading] = useState(false);
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  const [credits, setCredits] = useState(0);
  const [showCreditAlert, setShowCreditAlert] = useState(false);
  
  const dropdownRef = useRef(null);
  const { currentUser } = useAuth();

  useEffect(() => {
    if (currentUser) {
      const userDocRef = doc(db, 'users', currentUser.uid);
      const unsubscribe = onSnapshot(userDocRef, (docSnapshot) => {
        if (docSnapshot.exists()) {
          const userData = docSnapshot.data();
          setCredits(userData.credits || 0);
        }
      });
      return () => unsubscribe();
    }
  }, [currentUser]);

  useEffect(() => {
    const handleClickOutside = (event) => {
      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
        setIsDropdownOpen(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);

  const onDrop = useCallback((acceptedFiles) => {
    setImage(acceptedFiles[0]);
  }, []);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: { 'image/*': ['.jpeg', '.jpg', '.png', '.gif', '.webp'] },
    maxFiles: 1,
    disabled: !!image
  });

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!image) {
      alert('Please select an image first.');
      return;
    }
    if (credits === 0) {
      setShowCreditAlert(true);
      return;
    }

    setLoading(true);

    try {
      const formData = new FormData();
      formData.append('image', image);
      formData.append('style', style);

      console.log('FormData content:');
      for (let [key, value] of formData.entries()) {
        console.log(key, value);
      }

      const response = await axios.post(`${API_URL}/generate`, formData,);

      const generatedImageUrl = response.data.output;

      // Sauvegarder les images dans Firebase Storage
      console.log('start save dans FBS');
      const originalImageRef = ref(storage, `original/${currentUser.uid}/${Date.now()}_${image.name}`);
      console.log('Saved dans FBS');
      await uploadBytes(originalImageRef, image);
      const originalImageUrl = await getDownloadURL(originalImageRef);

      const generatedImageResponse = await fetch(generatedImageUrl);
      const generatedImageBlob = await generatedImageResponse.blob();
      const generatedImageRef = ref(storage, `generated/${currentUser.uid}/${Date.now()}_generated.png`);
      await uploadBytes(generatedImageRef, generatedImageBlob);
      const firebaseGeneratedImageUrl = await getDownloadURL(generatedImageRef);

      // Sauvegarder les informations dans Firestore
      console.log('Save dans FBS');
      const setupsRef = collection(db, 'generatedImages');
      const setupsSnapshot = await getDocs(query(setupsRef, where('userId', '==', currentUser.uid)));
      const setupNumber = setupsSnapshot.size + 1;
      console.log('Saved dans FBS');

      await addDoc(setupsRef, {
        userId: currentUser.uid,
        originalImage: originalImageUrl,
        generatedImage: firebaseGeneratedImageUrl,
        style: style,
        title: `Setup #${setupNumber}`,
        createdAt: new Date()
      });

      // Mettre à jour les crédits de l'utilisateur
      const userRef = doc(db, 'users', currentUser.uid);
      await updateDoc(userRef, { credits: increment(-1) });

      setLoading(false);
      alert('Image generated and saved successfully!');
      
      setImage(null);
      setStyle('Gamer');
    } catch (error) {
      console.error('Error:', error.response ? error.response.data : error.message);
      setLoading(false);
      alert('An error occurred. Please try again.');
    }
  };

  const styles = ['Gamer', 'Aesthetic', 'RGB Free'];
5
  • 1
    Your code contains no console.log that would output an array. What do you really get? Commented Sep 15, 2024 at 11:48
  • thanks for your answer, just cause I deleted this console.log after so many things tried. Actually I have in the logs: File details: No file. Sounds weird!
    – nonomg
    Commented Sep 15, 2024 at 11:59
  • What are you doing the upload the file? The problem could be there instead. Please edit the question to show all relevant code and instructions to reproduce the problem - we should be able to copy what you have and run it for ourselves to get the same result that you describe. Commented Sep 15, 2024 at 12:38
  • 1
    Done, added client side code, with how it works!
    – nonomg
    Commented Sep 15, 2024 at 14:02
  • try checking frontend code, try logging file, or maybe try hardcoding file, it could be the drag-n-drop react hook. also, try making request with fetch
    – traynor
    Commented Sep 15, 2024 at 19:22

1 Answer 1

0

It appears your issue is with how you're calling the middleware within your express app. In your code your req object is defined from the middleware before multer, so will only have raw data (as the multer middleware won't have run yet).

Instead of trying to call the multer middleware within your post function, use it as a middleware before. Then the request object passed to the post callback function will have the file you're expecting.

...
const multer = require('multer');
...

// Configuration de Multer avec gestion d'erreurs
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 }, // Limite à 10MB
}).single('image');

...
//post request using multer middleware
app.post('/generate', upload, (req, res) => {
    try {
      const { file } = req;
      const { style } = req.body;
      ...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.