【发布时间】:2018-05-02 10:22:44
【问题描述】:
我花了很多时间调试这个问题,我得出的结论是它与我在我的 express 服务器的 POST 和 GET 路由中使用的 firebase 数据库引用有关。以下是完整的代码。我的前端调用的第一个路由是router.get('/get-images', function(req, res) ... 请注意,在回调函数中,我首先设置了引用firebaseApp.database().ref(/user_profile_images/${userId})。这段代码从头到尾都运行良好。
但是后来我调用了 POST 路由 router.post('/add-images', upload.single('userImage'), function(req, res) ...,这就是事情开始变得非常奇怪的地方 - 一旦代码执行到使用 Firebase 引用的地步 firebaseApp.database().ref(user_profile_images/${userId}).push()...在这一点上,回调函数似乎在之前的某个时间点首先在 GET 路由中实现,正在被调用,这就是我完全不知道发生了什么的地方。
以下是我的完整服务器代码,然后是示例输出,其中我已将 console.logs 设置为“映射”函数中的每个步骤 - 当行从 ADD IMAGES 第 5 部分更改为 GET IMAGES 第 1 部分时,请注意输出- 这就是来自 POST 的执行代码似乎在 GET 路由的引用回调中调用代码的点
ADD IMAGES part 4
ADD IMAGES part 5
GET IMAGES part 1
GET IMAGES part 2
完整的服务器代码(减去有效密钥/数据库名称/等):
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...');
这是我代码中每个 console.log() 的命令行输出:
//////////////////////////////////////
///////////////////////////////////////
///////////////////////////////////////
///////////////////////////////////////
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)
重要提示:如果我从未在我的应用程序的生命周期中调用 GET 路由,那么 POST 路由可以正常执行直到完成......
【问题讨论】:
标签: javascript node.js reactjs express cloudinary