Не удалось получить двоичные данные в ASP. NET Core 3 из клиента HTML / JS через nodejs express - PullRequest
0 голосов
/ 16 января 2020

У меня есть HTML / JS клиент сторона, которая связывается со nodejs / express сервер сторона. Клиент отправляет файл на сервер узла - пока все работает нормально. Сервер узла вызывает https.request для отправки двоичных данных на nother ASP. NET Core 3 компонент (сервер). Проблема в том, что контроллер ASP. NET Core 3.1 продолжает получать ошибки / пустые данные. API: TransferModel из controller.cs либо получает пустой массив данных, либо генерирует исключения (зависит от изменений, которые я делаю в nodejs)

Client / HTML Side

<label style="font-weight: bold;">Upload Model File...</label>
    <form action="fileupload" method="post" enctype="multipart/form-data">
      <input type="file" name="filetoupload"><br>
      <br><input class="button" type="submit">
    </form>

Node JS Завершить - частичное

var express = require('express');
const path = require('path');
var bodyParser = require("body-parser");
const plantAPI = require('./plantSimAPI')
var formidable = require('formidable');

var app = express();
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

var serverUrl = 'localhost'
var port = '5000'

app.post('/fileupload', (req, res) => {
  var form = new formidable.IncomingForm();
  form.keepExtensions = true;
  form.onPart = function (part) {
    if (!part.filename) {
      // let formidable handle all non-file parts
      form.handlePart(part);
      return;
    }
    part.on('data', function (data) {
      transferModel(data);    
    });
    part.on('end', function () {
      transferModel([]);
      res.send(200);
    });
    part.on('error', function (err) {
      // handle this too
      res.json(err);
    });
  }

  form.parse(req, function (err, fields, files) {
    //pass the file to the plant sim server
    // plantSim.
      //res.send(200);
      console.log('ahmad');
  });
});


    function transferModel(data, ondone) {
        this.callPlantSimAPI2('/plantsim/api/model/transfer', 'PUT', data, () => {
            if (ondone) ondone();
        });

    }
    
    function callPlantSimAPI2(path, method, data, onDone) {
        const options = {
            host: serverUrl,
            port: port,
            path: path,
            method: method,
            agent: new https.Agent({
                rejectUnauthorized: false,
            }),
            headers: {
                "Content-Type": "multipart/form-data" //"application/octet-stream"
            }
        };

        var req = https.request(options, function (res) {
            console.log('STATUS: ' + res.statusCode);
            console.log('HEADERS: ' + JSON.stringify(res.headers));
            res.setEncoding('utf8');
            res.on('data', function (chunk) {
                console.log('BODY: ' + path + ' : ' + chunk);
                if (onDone) onDone(chunk);
            });
            res.on('error', (err) => {
                if (onDone) onDone("")
            });
            res.on('end', () => {
                if (onDone) onDone("")
            })
        });

        req.on('error', function (e) {
            console.log('problem with request: ' + e.message);
            if (onDone) onDone("");
        });

        // write data to request body
        if(data && data.length>0) req.write(data);
        req.end();
    }

ASP. NET Web API Core3.1

Startup.cs

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors();
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            //first routing
            app.UseRouting();
            //then
            app.UseCors(
                options => options.SetIsOriginAllowed(x => _ = true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()
            );
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

        }

Controller.cs

namespace PlantSimAPI.Controllers
{
    [ApiController]
    [EnableCors("simapi")]
    [Route("plantsim/api")]
    public class PlantSimAPIController : ControllerBase
    {
        [HttpPut]
        [Route("model/transfer")]
        public async Task<IActionResult> TransferModel( [FromBody]byte[] data)
        {
             try
                {
                    //byte[] data = { };
                    using (var ms = new MemoryStream(1024*8))
                    {
                        await Request.Body.CopyToAsync(ms);
                        data = ms.ToArray();
                        //do something with my data
                    }
                    return Ok();
                }
                catch (Exception ex)
                {
                    //Log message
                    this._logger.LogError(ex.Message);
                    //return bad result
                    return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
                }
        }
    }
}

1 Ответ

0 голосов
/ 26 января 2020

После большого количества поиска ... и множества проблем во время решения вот что я получаю в итоге:

  1. Nodejs сервер:

удалить :

headers: { "Content-Type": "multipart/form-data" //"application/octet-stream" }

из опций

Многопоточность:

из-за получения данных кусками - вот как nodejs отправляет данные в ядро ​​ASP. NET (или вообще), я заканчиваю с выставлением двух API:

  • nodejs кусок:

app.post('/fileupload', (req, res) => {
  var form = new formidable.IncomingForm();
  form.keepExtensions = true;
  form.multiples = false;
  var ind = 0;
  form.onPart = function (part) {
    if (!part.filename) {
      // let formidable handle all non-file parts
      form.handlePart(part);
      return;
    }
    part.on('data', function (data) {
      plantSim.transferModel(data, ind++);
    });
    part.on('end', function () {
      plantSim.transferModelCompleted([], ind);
      res.send(200);
    });
    part.on('error', function (err) {
      // handle this too
      res.json(err);
    });
  }

Обратите внимание, что у нас есть первый API для передачи данных с помощью блока данных и его индекса.

Затем у нас есть другие данные, которые не передают ничего, кроме общего числа чанков.

  • . NET Базовый API: один API для получения данных с индексом чанка, другой API с общим количеством чанков.

При получении данные (TransferT (данные, индекс)) мы помещаем данные в словарь и при получении вызова TransferComplete (счет), мы сохраняем количество кусков.

, так как все асин c, после передачи и TransferComplete проверяет, равна ли длина Dictoinary == количеству фрагментов, и только затем сортируем словарь и сохраняем файл на диске.

...