2017-11-18 1 views
0

Ich habe viel Zeit damit verbracht, dieses Problem zu debuggen, und ich bin zu der Schlussfolgerung gekommen, dass es sich um die Firebase-Datenbankreferenz handelt, die ich in meinen POST- und GET-Routen meines Express-Servers verwende. Im Folgenden finden Sie den vollständigen Code. Die allererste Route, die von meinem Frontend aufgerufen wird, ist router.get('/get-images', function(req, res) .... Beachten Sie, dass ich in der Callback-Funktion zuerst die Referenz firebaseApp.database().ref(/user_profile_images/${userId}) eingerichtet habe. Dieser Code funktioniert von Anfang bis Ende.Node Express + Firebase-Admin-Server - Firebase-Datenbank-Referenz in POST-Aufruf-Code in GET - warum?

jedoch später ich die POST Route router.post('/add-images', upload.single('userImage'), function(req, res) ... nennen und das ist, wo die Dinge beginnen, wirklich seltsam bekommen - sobald die Ausführung von Code auf den Punkt, wo die Firebase Referenz firebaseApp.database().ref( user_profile_images verwendet wird/$ {userId} ).push()... es scheint an dieser Stelle Die Callback-Funktion, die zuvor in der GET-Route implementiert wurde, wird aufgerufen, und das ist der Punkt, an dem ich überhaupt nicht weiß, was passiert.

Nachstehend ist mein vollständiger Servercode, gefolgt von einem Beispieloutput, wo ich console.logs eingerichtet habe, um jeden Schritt in der Funktion zu "mappen" - achten Sie auf die Ausgabe, wenn sich die Zeilen von ADD IMAGES part 5 to GET IMAGES part ändern 1 - das ist der Punkt, dass die Ausführung Code aus der POST-Code in der Referenz Rückruf in der GET-Route zu rufen scheint

ADD IMAGES part 4 
ADD IMAGES part 5 
GET IMAGES part 1 
GET IMAGES part 2 

kompletter Servercode (minus gültige Schlüssel/db Namen/etc):

let express = require('express'); 
let cors = require('cors'); 
let bodyParser = require('body-parser'); 
const uuidv4 = require('uuid/v4'); 
let base64Img = require('base64-img'); 
var firebaseAdmin = require("firebase-admin"); 
let cloudinary = require('cloudinary'); 
let multer = require('multer'); 
let UserProfileImage = require('./utils/UserProfileImage'); 
let _ = require('lodash'); 


/** 
* ======================== 
* firebase set up 
* 
* @type  {Function} 
*/ 
var firebaseServiceAccount = require('./somekeyfile.json'); 

let firebaseApp = firebaseAdmin.initializeApp({ 
    credential: firebaseAdmin.credential.cert(firebaseServiceAccount), 
    databaseURL: 'https://somedb.firebaseio.com/' 
}); 
// ======================== 


/** 
* ======================== 
* couldinary configuration 
*/ 
cloudinary.config({ 
    cloud_name: 'somecloudname', 
    api_key: 'somekey', 
    api_secret: 'somesecret' 
}); 

const CLOUDINARY_UPLOAD_PRESET = 'veeezmct'; 
let port = process.env.PORT || 8080; 
// ======================== 



// ROUTES FOR OUR API 
// =================================== 



// get an instance of the express Router 
let router = express.Router(); 


// test route to make sure everything is working 
// (accessed at POST http://localhost:8080/image-controller-api/upload) 
let upload = multer({ 
    dest: 'uploads/', 
    limits: { 
     fileSize: 5 * 1024 * 1024 // no larger than 5mb, you can change as needed. 
    } 
}); 
router.post('/add-images', upload.single('userImage'), function(req, res) { 

// console.log("\n########################\n########################\n########################\n"); 
console.log('ADD IMAGES'); 

// make sure there is a user id before continuing 
if (req.body.userId === undefined || req.file === undefined) { 
    return res.json({message: 'User ID and image file required to proceed'}); 
} 

console.log('ADD IMAGES part 2'); 

// get the FB userId fromt he request body 
const userId = req.body.userId; 
const position = req.body.position; 
const isPrimary = req.body.isPrimary; 
let file = req.file; 

console.log('ADD IMAGES part 3'); 

let uploadType = 'authenticated'; 
cloudinary.v2.uploader.upload(
    req.file.path, 
    { 
     upload_preset: CLOUDINARY_UPLOAD_PRESET, 
     type: uploadType, 
     sign_url: true, 
     folder: userId 
    }, 
    function(error, result) { 

     console.log('ADD IMAGES part 4'); 

     // need to save the image url in the firebase 
     // user record here - create a new user profile image 
     //let ref = firebaseApp.database().ref(`user_profile_images/${userId}`); 
     let userProfileImage = new UserProfileImage(); 
     userProfileImage.resourceId = result.public_id; 
     userProfileImage.userId = userId; 
     userProfileImage.url = result.secure_url; 
     userProfileImage.position = position; 
     userProfileImage.isPrimary = isPrimary; 
     userProfileImage.isPrivate = "false"; 

     console.log('ADD IMAGES part 5'); 


     // use 'child' and 'set' combination to save data 
     // in your own generated key 
     firebaseApp.database().ref(`user_profile_images/${userId}`) 
      .push().update(userProfileImage).then(function(ref1) { 
       console.log('ADD IMAGES part 6'); 

       base64Img.requestBase64(
        result.secure_url, 
        function(err, messageRes, body) { 
         console.log('ADD IMAGES part 7'); 

         if (!err) { 
          console.log('SUCCESS ENCODING IMAGE'); 
          return res.json({imageData: body}); 
         } else { 
          console.log('ERROR ENCODING IMAGE'); 
          return res.json({error: err}); 
         } 

        } 
       ); 


     }, function(error) { 
      console.log('ERROR AT END', error); 
     }); 

    } 
); 

}); 


/** 
* get user profile images by user ID 
**/ 
router.get('/get-images', function(req, res) { 

console.log("\n///////////////////////////////////////\n///////////////////////////////////////\n///////////////////////////////////////\n///////////////////////////////////////\n"); 

console.log('GET IMAGES'); 

let userId = req.query.userId; 
firebaseApp.database().ref(`user_profile_images/${userId}`).on(
     'value', 
     snapshot => { 

      console.log('GET IMAGES part 1'); 

      // if we have valid objects to iterate through 
      if (snapshot.val()) { 
       console.log('GET IMAGES part 2'); 

       // iterate through the objects to get each image url 
       // and base64 representation 

       // get the userProfileImages object list 
       let userProfileImages = snapshot.val(); 
       // flag to determine how many requests have been completed 
       let completeRequests = 0; 
       // the total number of request to make which is the total 
       // number of userProfileImages to process 
       let numberOfRequestsToMake = Object.keys(userProfileImages).length; 

       // iterate through each of the user profile images 
       console.log('GET IMAGES part 3'); 
       _.map(userProfileImages, (userProfileImage, key) => { 

        console.log('GET IMAGES LOOP ', completeRequests); 
        completeRequests++; 

        if (completeRequests === numberOfRequestsToMake) { 
         console.log('GET IMAGES part 4'); 
         console.log('# of requests complete, return to client'); 
         return res.json({ userProfileImages: userProfileImages }); 
        } 

       }); // end _.map 

      } else { 
       console.log('ok sending default empty array'); 
       return res.json({userProfileImages: {}}); 
      } 

     }, 
     errorObject => { 
      console.log('base64Img.requestBase64 result error'); 
      return res.json({error: error}); 
     } 
    ); 

}); 


// set up the server 
let app = express(); 

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


// REGISTER OUR ROUTES 
// all of our routes will be prefixed with /image-controller-api 
app.use('/image-controller-api', router); 


// START THE SERVER 
app.listen(port); 
console.log('Listening...'); 

Hier ist die Befehlszeilenausgabe von jedem der c (Onsole.log) in meinem Code:

////////////////////////////////////// 
/////////////////////////////////////// 
/////////////////////////////////////// 
/////////////////////////////////////// 

GET IMAGES 
GET IMAGES part 1 
GET IMAGES part 2 
GET IMAGES part 3 
GET IMAGES LOOP 0 
GET IMAGES LOOP 1 
GET IMAGES LOOP 2 
GET IMAGES LOOP 3 
GET IMAGES LOOP 4 
GET IMAGES part 4 
# of requests complete, return to client 
ADD IMAGES 
ADD IMAGES part 2 
ADD IMAGES part 3 
ADD IMAGES part 4 
ADD IMAGES part 5 
GET IMAGES part 1 
GET IMAGES part 2 
GET IMAGES part 3 
GET IMAGES LOOP 0 
GET IMAGES LOOP 1 
GET IMAGES LOOP 2 
GET IMAGES LOOP 3 
GET IMAGES LOOP 4 
GET IMAGES LOOP 5 
GET IMAGES part 4 
# of requests complete, return to client 
FIREBASE WARNING: Exception was thrown by user callback. Error: Can't set headers after they are sent. 
at ServerResponse.setHeader (_http_outgoing.js:371:11) 

Wichtiger Hinweis: Wenn ich nie die GET-Route in der lifecyle meiner app nennt, dann ist die POST-Route führt fein bis zur Fertigstellung ....

Antwort

0

Des Problem ist dieses Bit:

Sie registrieren einen Listener für Änderungen an den Bildern, aber nie entfernen. Der Listener wird zunächst mit dem aktuellen Wert aufgerufen, wird dann aber jedes Mal aufgerufen, wenn sich der Wert ändert. Ihr POST-Handler ändert den Wert, sodass das Ereignis ausgelöst wird.

Um dieses Problem zu beheben, verwenden Sie once anstelle von on.

+0

Das war es ... Ich lerne gerade das und ich bin nicht der beste JS-Programmierer, ich bin ein PHP-Typ - immer noch versuche ich meinen Kopf um asynchrone Ausführungen von JS zu wickeln. Danke für die Hilfe. –