Upload files with multer/Angular

307 Views Asked by At

Error: Multipart: Boundary not found

I want to add file with some text files with angular: here's my service:

src/app/services/web.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';


const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'multipart/form-data'
  })
}

@Injectable({
  providedIn: 'root'
})
export class WebService {

  devUri:string = 'http://localhost:4000';
  constructor(private http:HttpClient) { }
    
  post(uri:string, object: Object){
    console.log(object);
    return this.http.post(`${this.devUri}/${uri}`, object, httpOptions);
  }

}

It shows Error: Multipart: Boundary not found Before somebody write "Just remove 'Content-Type': 'multipart/form-data' it will work it won't! When I remove this, then my req.files = []( there's also some variations where this is equal undefined). Here's my route( not full, but there's nothing more is needed):

routes/api/messages.js

const { initStorage, initUpload } = require('../../modules/multerModule');

const conn = mongoose.connection;
Grid.mongo = mongoose.mongo;
// Init gfs
let gfs;

conn.once('open', () => {
  // Init stream
  gfs = Grid(conn.db);
  gfs.collection(collectionName);
});

const collectionName = 'messages';
const bucketName = 'messages';

const storage = initStorage(conn, bucketName);

const upload = initUpload(storage);

router.post('/', upload.any(), (req, res) => {

  console.log(req.files);
  console.log(req.body);

...
}

Here's my functions from multerModule(it all works on react/redux, app, but don't work on Angular):

modules/multerModule.js:

const path = require('path');
const multer = require('multer');
const crypto = require('crypto');
const GridFsStorage = require('multer-gridfs-storage');

// Create storage engine
const initStorage = (conn, bucketName) => new GridFsStorage({
  db: conn,
  file: (req, file) => {
    return new Promise((resolve, reject) => {
      crypto.randomBytes(16, (err, buf) => {
        if (err) {
          return reject(err);
        }
        const filename = buf.toString('hex') + path.extname(file.originalname);
        const fileInfo = {
          filename: filename,
          bucketName: bucketName
        };
        resolve(fileInfo);
      });
    });
  }
});

// Create upload module
const initUpload = (storage) => multer({
  storage: storage,
  fileFilter: function (req, file, callback) {
    const ext = path.extname(file.originalname);
    if (ext !== '.png' && ext !== '.jpg' && ext !== '.gif' && ext !== '.jpeg') {
      return callback(new Error('Only images are allowed'))
    }
    callback(null, true)
  }
});

module.exports = { initStorage, initUpload };

Ofcourse later when I try to use req.files(destructurize or whatever else) I get error, because it's undefined or empty array. And in req.body, theres also shows up messages field:

[Object: null prototype] {
  messages: '[object File]',
  content: 'dsada',
  path: 'undefined',
  fileImage: 'true'
}

The last two things are server.js and onSubmit function:

server.js

...
const bodyParser = require('body-parser');
// #6 Initialize an Express application 
const app = express();

app.use(cors()); 

app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(bodyParser.json({limit: '50mb', extended: true}));

const db = process.env.mongoURI;

// Connect to Mongo
mongoose 
  .connect(db, { 
    useNewUrlParser: true,
    useCreateIndex: true, 
    useUnifiedTopology: true, 
    useFindAndModify: false }) // Adding new mongo url parser 
  .then(() => console.log('MongoDB Connected...')) 
  .catch(err => console.log(err)); 
...

OnSubmit function:

src/app/components/Message/add-message/add-message.ts

onSubmit() {

    if( this.fileImage){
      const message = new FormData();
      

      console.log(this.files[0]);
      message.append('messages', this.files);

      console.log(typeof(JSON.stringify(this.fileImage)));
      message.append('content', this.content);
      message.append('fileImage', JSON.stringify(this.fileImage));
  

this.addMessage.emit(message);
}
else{
  const message = {
    content: this.content,
    fileImage: this.fileImage,
    path: this.path
  }
  this.addMessage.emit(message);
}

}

After some tries I get another error( now I don't include httpOptions in HttpClient module and the file data appended to FormData() is identical as this in react-redux/node app):

Error: The database connection must be open to store files
    at GridFSStorage._handleFile

The problem is that the same code works correctly in redux app, but not in angular.

1

There are 1 best solutions below

0
On

Ok, I found something when I console.log here:

conn.once('open', () => {
  // Init stream
  gfs = Grid(conn.db);
  
  gfs.collection(collectionName);
});

const collectionName = 'messages';
const bucketName = 'messages';

console.log(conn); 
...

I found one difference:

On angular app I found this:

models: { user: Model { user } }

I have three models: user, post and message.

On react app I found that:

 models: {
     announcement: Model { announcement },
     user: Model { user },
     slide: Model { slide },
     content: Model { content },
     insurance: Model { insurance },
     contact: Model { contact }
   }

and it's correct, so it looks like my storage doesn't includes all models routes. And I don't know why, but it seems, that's the problem. The same goes about modelSchemas properties. Even when I comment this route( app.use(/auth) ) it still includes only userSchema and UserModel.

And after connection is server.js I logged the "promise".

// Connect to Mongo
const promise = mongoose 
  .connect(db, { 
    useNewUrlParser: true,
    useCreateIndex: true, 
    useUnifiedTopology: true, 
    useFindAndModify: false }) // Adding new mongo url parser 
  .then(() => console.log('MongoDB Connected...')) 
  .catch(err => console.log(err)); 

  console.log(promise);

And get only one model and schema(even I have three models: post, user, message):

Promise { <pending> }
NativeConnection {
  base: Mongoose {
    connections: [ [Circular] ],
    models: { user: Model { user } },
    modelSchemas: { user: [Schema] },
    options: { pluralization: true, [Symbol(mongoose:default)]: true },
    _pluralize: [Function: pluralize],
    Schema: [Function: Schema] {
      reserved: [Object: null prototype],
      Types: [Object],
      ObjectId: [Function]
    },
    model: [Function],
    plugins: [ [Array], [Array], [Array], [Array], [Array] ]
  },
  collections: {
    users: NativeCollection {
      collection: null,
      Promise: [Function: Promise],
      _closed: false,
      opts: [Object],
      name: 'users',
      collectionName: 'users',
      conn: [Circular],
      queue: [],
      buffer: true,
      emitter: [EventEmitter]
    }
  },
  models: { user: Model { user } },
  config: { autoIndex: true, useCreateIndex: true, useFindAndModify: false },
  replica: false,
  options: null,
  otherDbs: [],
  relatedDbs: {},
  states: [Object: null prototype] {
    '0': 'disconnected',
    '1': 'connected',
    '2': 'connecting',
    '3': 'disconnecting',
    '99': 'uninitialized',
    disconnected: 0,
    connected: 1,
    connecting: 2,
    disconnecting: 3,
    uninitialized: 99
  },
  _readyState: 2,
  _closeCalled: false,
  _hasOpened: false,
  plugins: [],
  id: 0,
  _listening: false,
  _connectionString: 'Sorry, but cannot pass :)',
  _connectionOptions: {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    promiseLibrary: [Function: Promise],
    driverInfo: { name: 'Mongoose', version: '5.10.3' }
  },
  client: MongoClient {
    _events: [Object: null prototype] {},
    _eventsCount: 0,
    _maxListeners: undefined,
    s: {
      url: 'sorry, but cannot pass :)', 
      options: [Object],
      promiseLibrary: [Function: Promise],
      dbCache: Map {},
      sessions: Set {},
      writeConcern: undefined,
      namespace: [MongoDBNamespace]
    },
    [Symbol(kCapture)]: false
  },
  '$initialConnection': Promise { <pending> },
  then: [Function],
  catch: [Function],
  _events: [Object: null prototype] {
    open: [Function: bound onceWrapper] { listener: [Function] }
  },
  _eventsCount: 1