首页 > 解决方案 > 使用 multer/Angular 上传文件

问题描述

错误:多部分:未找到边界

我想添加一些带有角度的文本文件的文件:这是我的服务:

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);
  }

}

它显示Error: Multipart: Boundary not found 在有人写之前“只需删除'Content-Type':'multipart / form-data'它会起作用它不会!当我删除它时,我的req.files = [](这里还有一些变化等于未定义)。这是我的路线(不完整,但不需要更多):

路线/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);

...
}

这是我来自 multerModule 的函数(它们都适用于 react/redux、app,但不适用于 Angular):

模块/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 后来当我尝试使用 req.files(destructurize 或其他) 我得到错误,因为它是未定义或空数组。在 req.body 中,还显示了 messages 字段:

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

最后两件事是 server.js 和 onSubmit 函数:

服务器.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 函数:

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);
}

}

经过一些尝试后,我得到另一个错误(现在我在 HttpClient 模块中不包含 httpOptions 并且附加到 FormData() 的文件数据与 react-redux/node 应用程序中的相同):

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

问题是相同的代码在 redux 应用程序中可以正常工作,但不能在 angular 中正常工作。

标签: javascriptangularexpressmultermulter-gridfs-storage

解决方案


好的,我在这里 console.log 时发现了一些东西:

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

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

console.log(conn); 
...

我发现了一个区别:

在 Angular 应用程序上,我发现了这个:

models: { user: Model { user } }

我有三个模型:用户、帖子和消息。

在反应应用程序上,我发现:

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

这是正确的,所以看起来我的存储不包括所有模型路线。我不知道为什么,但似乎这就是问题所在。modelSchemas 属性也是如此。即使我评论这条路线( app.use(/auth) ),它仍然只包含 userSchema 和 UserModel。

在连接是 server.js 之后,我记录了“承诺”。

// 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);

并且只得到一个模型和模式(即使我有三个模型: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

推荐阅读