'TypeError: Cannot read property 'then' of undefined , TypeError: _cb is not a function

my code in github, but I didn't upload the latest version: https://github.com/godzillalogan/markdownblog

I use node.js and express.js framework. Database use mongoDB. I am using the package Imgur and dotenv, try to upload image to imgur.

But I encounter some bug:

bug:

App is running on http://localhost:3000
mongodb connected!
TypeError: Cannot read property 'then' of undefined
    at Promise (D:\Github\markdownblog\helpers\file-helpers.js:26:7)
    at new Promise (<anonymous>)
    at imgurFileHandler (D:\Github\markdownblog\helpers\file-helpers.js:23:10)
    at router.put (D:\Github\markdownblog\routes\modules\admin.js:115:26)
    at process._tickCallback (internal/process/next_tick.js:68:7)
D:\Github\markdownblog\node_modules\imgur-node-api\lib\imgur.js:34
        _cb(null, body);
        ^

TypeError: _cb is not a function
    at Request._callback (D:\Github\markdownblog\node_modules\imgur-node-api\lib\imgur.js:34:9)
    at Request.self.callback (D:\Github\markdownblog\node_modules\request\index.js:142:22)
    at Request.emit (events.js:182:13)
    at Request.<anonymous> (D:\Github\markdownblog\node_modules\request\index.js:856:14)
    at Request.emit (events.js:187:15)
    at IncomingMessage.<anonymous> (D:\Github\markdownblog\node_modules\request\index.js:808:12)
    at IncomingMessage.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1094:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
[nodemon] app crashed - waiting for file changes before starting...

Here is my code:

app.js:

const path = require('path') // 引入 path 套件
const express = require('express')
const session = require('express-session')

const bodyParser = require('body-parser')  //新版express以內建body-parser
const { engine } = require('express-handlebars');
const flash = require('connect-flash')
if (process.env.NODE_ENV !== 'production') {  //要放在const routes = require('./routes')前面
  require('dotenv').config()
}

const routes = require('./routes')

const app = express()
const PORT = process.env.PORT || 3000
const methodOverride = require('method-override')  // 載入 method-override
//others code............

helpers/file-helpers.js:

const fs = require('fs') // 引入 fs 模組, fs 模組是 Node.js 提供專門來處理檔案的原生模組


//載入 imgur 套件
const imgur = require('imgur-node-api')

const IMGUR_CLIENT_ID = process.env.IMGUR_CLIENT_ID
console.log('IMGUR_CLIENT_ID:',IMGUR_CLIENT_ID)
imgur.setClientID(IMGUR_CLIENT_ID)

const localFileHandler =  file => { // file 是 multer 處理完的檔案
  return new Promise((resolve, reject) => {
    if (!file) return resolve(null) 
    const fileName = `upload/${file.originalname}`
    return fs.promises.readFile(file.path)
      .then(data => fs.promises.writeFile(fileName, data))
      .then(() => resolve(`/${fileName}`))
      .catch(err => reject(err))
  })
}

const imgurFileHandler = file => {
  return new Promise((resolve, reject) => {
    if (!file) return resolve(null)
    return imgur.upload(file.path)
      .then(img => {
        // resolve(img?.link || null) // 檢查 img 是否存在
        resolve(img ? img.link : null)
      })
      .catch(err => reject(err))
  })
}
module.exports = {
  localFileHandler,
  imgurFileHandler  //img
}

routes/modules/admin:

const express = require('express')
const router = express.Router()
const Article = require('../../models/article');
const Category = require('../../models/category');
const User = require('../../models/user');
const Contact = require('../../models/contact');
const upload = require('../../middleware/multer') // 載入 multer
const { imgurFileHandler } = require('../../helpers/file-helpers') // 將 file-helper 載進來

////others code....

//edit user
router.put('/users/:id', upload.single('avatar'), async (req, res)=>{
  try{
  console.log('有到edit user嗎')
  const _id = req.params.id
  const { name,avatar,introduction } = req.body
  const { file } = req // 把檔案取出來
 
  const user = await User.findOne({ _id})
  const filePath = await imgurFileHandler(file) // 把檔案傳到 file-helper 處理 
  
  user.name = name
  // user.cover = filePath || user.cover
  user.avatar = filePath || user.avatar
  user.introduction = introduction
  await user.save()
  res.redirect('/about')
  }catch(e){
    console.log(e)
    res.redirect(`/admin/users`)
  }
  // const {title,description,markdown} = req.body   //和new一樣才能將markdown轉成html
  //   Article.create({...req.body})
  //   res.redirect('/')


////others code....
})


What I have try:

I think it is about promise problem, and localFileHandler in helpers/file-helpers.js is work successful , but why imgurFileHandler in helpers/file-helpers.js is not work .

Thank you for your help.



Solution 1:[1]

I move it from helpers/file-helpers.js to routes/modules/admin.js. And the way is success. But I think there would have another better way.

routes/modules/admin.js

I add Imgur to the route in create article,edit article and edit user.

routes/modules/admin.js

//Create
router.post('/articles', upload.single('image'), async (req,res)=>{
  // console.log(req.body)
  // const article = new Article({
  //     title: req.body.title,
  //     description: req.body.description,
  //     markdown: req.body.markdown
  // });
  try{
    const {title,category,description,markdown} = req.body
    const{file} = req

    console.log('file:',file)
    if (file){
      imgur.setClientID(IMGUR_CLIENT_ID)
      imgur.upload(file.path,(err, img) =>{
        Article.create({...req.body, image: file ? img.data.link: null})
      })
    }else{
      Article.create({...req.body})
    }
   
    
    res.redirect('/admin/articles')
  } catch(e){
    console.log(e)
    res.render('admin/articles',{ article })
  }
})

////Update
//?edit?
router.get('/articles/edit/:id', async (req, res) => {
  // const _id = req.params.id
  // return Article.findOne({ _id})
  //   .lean()
  //   .then((article) => res.render('edit', { article}))
  //   .catch(error => console.log(error))
  try{
  const _id = req.params.id
  const article =  await Article.findOne({ _id}).lean()
  const categories = await Category.find().lean().sort({createdAt:'desc'})
  res.render('edit', { article,categories})
  }catch{
    console.log(e)
    res.redirect(`/articles/edit/:id`)
  }
})

//edit article
router.put('/articles/:id', upload.single('image'), async (req, res)=>{

  try{
    const _id = req.params.id
    const { title,description,markdown,category } = req.body
    const { file } = req // ??????
    const article = await Article.findOne({ _id})
    if (file){
        // const filePath = await imgurFileHandler(file) // ????? file-helper ?? 
        imgur.setClientID(IMGUR_CLIENT_ID)
        imgur.upload(file.path,async (err, img) =>{
        // Article.update({...req.body, image: file ? img.data.link: article.image})
        article.title = title
        article.description = description
        article.markdown = markdown
        article.category = category
        article.image = img.data.link
        await article.save()
        })
    }else{
      article.title = title
      article.description = description
      article.markdown = markdown
      article.category = category
      await article.save()
    }
    res.redirect('/admin/articles')
  }catch(e){
    console.log(e)
    res.redirect(`/admin/articles`)
  }
  // const {title,description,markdown} = req.body   //?new?????markdown??html
  //   Article.create({...req.body})
  //   res.redirect('/')
})

Solution 2:[2]

It's probably because imgur.upload() dosent support promises and you should pass a callback as a second argument to the function like this :

imgur.upload(file.path, (err, response) => {
  if(err) {
    return null
  }
  return response.data.link
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ???
Solution 2 siamak