Angular SSR - отправка FormData на сервер узла через экспресс-API - PullRequest
0 голосов
/ 30 октября 2019

В приложении стека MEAN мне нужно отправить FormData (с файлом) на внутренний сервер и сохранить эту информацию соответственно в MongoDB. Вот Angular метод компонента, который передает Form -

  saveProduct(formDirective): void {
    let formData: FormData = new FormData();
    formData.append("name", formModel.nameFormControl as string);
    formData.append("price", formModel.priceFormControl as string);
    formData.append("currency", formModel.currencyFormControl as string);
    formData.append("url", formModel.urlFormControl as string);
    formData.append("description", formModel.descriptionFormControl as string);

    if (this.fileToUpload !== null) {
      formData.append("uploadFile", this.fileToUpload, this.fileToUpload.name);
    }

    let headers = new Headers();
    headers.append("Content-Type", "multipart/form-data");
    headers.append("Accept", "application/json");
    headers.append("Authorization", this.jwtToken);

    this.productsService.addProduct(formData, headers).subscribe(res => {.......});
  }

  handleFileInput(files: FileList) {
    if (files.length > 0) {
      this.fileToUpload = files.item(0);
    }
  }

Вот Component HTML для файла формы, где File информация идеально добавляется в FormData. Это проверено и проверено -

<input
  #file
  formControlName="imageFormControl"
  id="file"
  type="file"
  accept="image/x-png,image/gif,image/jpeg"
  (change)="handleFileInput($event.target.files)"
/>

Вот Service метод, который отправляет FormData в API -

  addProduct(formData: FormData, headers: any): any {
    formData.forEach(entries => console.log(entries));
    return this.http
      .post(environment.apiURL + "/admin/product/save", formData, headers)
      .map(res => res);
  }

В console Я получаю FormData сFile информация.

Перейдем к Express API, где у меня возникла проблема

Я создал маршрут API в Angular SSR (сервер.ts). Вот как маршрут API добавляется в файл server.ts -

import { routes } from './api/routes';

app.use('/api', routes);

Структура файлов API имеет следующую иерархию -

api---
   ---functions
      admin.functions.ts
   ---routes
      admin.routes.ts
   ---routes.ts

Вот мой файл routes.ts -

import { Router } from 'express';

import AdminRoutes from './routes/admin.route';
export const routes = new Router();

// ADMIN ROUTES
routes.use('/admin', AdminRoutes);

export default routes;

Вот routes/admin.routes.ts код -

import {
  Router,
} from 'express';

import AdminAPI from '../functions/admin.functions';

const routes = new Router();

routes.route('/product/save').post(AdminAPI.saveProduct);

export default routes;

Вот functions/admin.functions.ts код -

import axios from 'axios';

class AdminAPI {

    async saveProduct(req, res, next) {
        try {
            console.log('admin save bot', `${SERVER}/admin/product/save`);
            console.log('body', req.body); <- Here I expected FormData
            const response = await axios.post(`${SERVER}/admin/product/save`, req.body, {headers: req.headers});
            res.json(response.data);
        } catch (error) {
            next(error);
        }
    }
}

export default new AdminAPI();

Здесь req.body пусто. То, что я ожидал в req.body, у меня будет FormData, но я не получу. Мне нужно отправить FormData информацию в бэкэнд через Axios

Для справки, давайте перейдем к серверному коду, который прекрасно работает, когда я не использую Angular SSR

exports.admin_save_default_product = function(req, res, next) {
  req.pipe(req.busboy);

  var product = {};

  req.busboy.on("field", function(key, value, keyTruncated, valueTruncated) {
    product[key] = value;
  });

  req.busboy.on("file", function(fieldname, file, filename) {
    let file_type = filename.split(".").pop();

    if (
      file_type === "png" ||
      file_type === "gif" ||
      file_type === "jpeg" ||
      file_type === "jpg"
    ) {
      let file_stream;
      let new_filename = Date.now() + "." + file_type;
      let filePath = path.join(__basedir, "/server/images", new_filename);

      fileUploaded = true;

      product.image = new_filename;

      file_stream = fs.createWriteStream(filePath);

      file.pipe(file_stream);

      file_stream.on("close", function() {
        console.log("file uploaded!");
      });
    }
  });

  req.busboy.on("finish", function() {
    let timestamp = new Date();
    let current_user_id = req._id;

    let current_user_promise = userModel.find_user_by_id(current_user_id);

    current_user_promise
      .then(function(result) {
        product.author = result;
        product.source = process.env.PRODUCT_DEFAULT_SOURCE;
        product.created_time = timestamp;
        product.bot_enabled = 0;

        let save_product_promise = productModel.save_product(product);

        save_product_promise
          .then(function(result) {
            var response_information = {
              message: "success"
            };

            res.status(201).json(response_information);
          })
          .catch(function(err) {
            res.status(500).json({ error: err });
          });
      })
      .catch(function(err) {
        res.status(500).json({ error: err });
      });
  });
};

Здесь busboy file and field события не работают, так как field и file пусты. Но finish событие работает нормально.

Итак, главная проблема, с которой я сталкиваюсь - не может правильно выполнить запрос Axios POST, который может отправить FormData в бэкэнд

Заранее спасибо!

...