Передача файлов между приложением AWS EC2 MVC и приложением Lambda Serverless Web API приводит к повреждению данных - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть приложение MVC, которое я размещаю на экземпляре Windows Server 2016 AWS EC2. Это приложение является инструментом администратора. Это приложение использует приложение Web API, которое размещено как приложение AWS Lambda Serverless (https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/lambda-build-test-severless-app.html).

Одна область моего приложения MVC позволяет пользователям загружать изображения с помощью ввода файла формы. Затем он отправляется обратно в контроллер MVC и отправляется в утилиту API, которая отправляет файл в API. Затем API изменяет размеры (используя Magick.NET) и сохраняет изображение в корзину S3 и полученный URL-адрес в базе данных MySQL.

Это все отлично работает при локальном запуске на моей машине. Проблема в том, когда я пытаюсь загрузить изображение на живой сайт. В результате, когда данные изображения загружаются в MagickImage, я получаю следующую ошибку:

ImageMagick.MagickCorruptImageErrorException: не файл JPEG: запускается с 0xef 0xbf `'@ error / jpeg.c / JPEGErrorHandler / 332 \ n

Я добавил некоторый код для регистрации первых 20 байтов данных (которые являются байтовым массивом) как в приложении MVC (до публикации файла в API), так и в API после получения файла. Я обнаружил, что полученные значения были совершенно разными, как показано ниже:

  • MVC: FF-D8-FF-E0-00-10-4A-46-49-46-00-01-01-01-01-2C-01-2C-00-00
  • API: EF-BF-BD-EF-BF-BD-EF-BF-BD-EF-BF-BD-00-10-4A-46-49-46-00-01

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

  • MVC: FF-D8-FF-E0-00-10-4A-46-49-46-00-01-01-01-01-2C-01-2C-00-00
  • API: FF-D8-FF-E0-00-10-4A-46-49-46-00-01-01-01-01-2C-01-2C-00-00

Есть ли какие-то настройки среды, которые мне нужно установить / изменить, что может быть причиной такого странного поведения?

Ниже приведены различные разделы кода, которые имеют отношение к порядку возникновения.

Контроллер MVC:

public async Task<IActionResult> AddImage(ImageFormViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return RedirectToAction("Details", new { id = viewModel.TourId, errors = string.Join(",", ViewData.ModelState.Values.SelectMany(x => x.Errors.Select(y => y.ErrorMessage))) });
    }

    var apiResponse = await this.api.PostFile<ApiResponse>($"tours/{viewModel.TourId}/images", viewModel.Image);
    if (apiResponse.Success)
    {
        return RedirectToAction("Details", new { id = viewModel.TourId, message = "Image added successfully!" });
    }
    {
        return RedirectToAction("Details", new { id = viewModel.TourId, errors = string.Join(",", apiResponse.Errors) });
    }
}

API Utility (в приложении MVC):

public async Task<TResponse> PostFile<TResponse>(string uri, IFormFile file) where TResponse : ApiResponse
{
    var response = default(TResponse);

    if (file != null && file.Length > 0)
    {
        var url = (!string.IsNullOrWhiteSpace(uri) ? new Uri(new Uri(this.baseUrl), uri) : new Uri(this.baseUrl)).ToString();

        using (var http = new HttpClient())
        {
            byte[] data;
            using (var stream = new MemoryStream())
            {
                await file.CopyToAsync(stream);
                data = stream.ToArray();
            }

            this.logger.Information("First bytes (MVC app): " + BitConverter.ToString(data.Take(20).ToArray()));

            var content = new MultipartFormDataContent();
            content.Add(new ByteArrayContent(data), "file", file.FileName);

            var httpResponse = await http.PostAsync(url, content);
            response = JsonConvert.DeserializeObject<TResponse>(await httpResponse.Content.ReadAsStringAsync());
        }
    }

    return response;
}

Контроллер API:

[HttpPost]
public async Task<IActionResult> Post([FromRoute]string tourId)
{
    var response = new ApiResponse();
    if (Request.HasFormContentType)
    {
        var form = Request.Form;
        foreach (var formFile in form.Files)
        {
            using (var stream = new MemoryStream())
            {
                await formFile.CopyToAsync(stream);
                var result = await this.tourManagementService.AddImage(HttpUtility.UrlDecode(tourId), Path.GetFileNameWithoutExtension(formFile.FileName), stream.ToArray());

                if (!result.Success)
                {
                    ...
                }
            }
        }
    }

    return Ok(response);
}

Сервисный метод для сохранения изображения и т. Д .:

public async Task<AddImageResult> AddImage(string tourId, string imageName, byte[] imageData)
{
    this.logger.Information("First bytes (API): " + BitConverter.ToString(imageData.Take(20).ToArray()));

    ...
}

Код, где используется Magick.NET и генерируется исключение:

private byte[] resizeImage(byte[] imageData, int width, int height)
{
    using (var image = new MagickImage(imageData, new MagickReadSettings { Format = MagickFormat.Jpeg }))
    {
        ...
    }
}

1 Ответ

0 голосов
/ 05 сентября 2018

Проблема оказалась в том, что мой AWS API Gateway не принимал двоичные данные. По умолчанию API-шлюз обрабатывает тело сообщения как JSON, как описано здесь - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html

По умолчанию API-шлюз обрабатывает тело сообщения как текстовое содержимое и применяет любой предварительно настроенный шаблон сопоставления для преобразования JSON строка.

Я считаю, что это источник коррупции. Чтобы исправить это, мне пришлось добавить «image / jpeg» в качестве допустимого двоичного типа носителя в API Gateway, показанном ниже:

enter image description here

Затем я настроил свой код так, чтобы он имел дело только с двоичными данными (и удалил содержимое содержимого формы):

Сторона MVC:

public async Task<TResponse> PostFile<TResponse>(string uri, IFormFile file) where TResponse : ApiResponse
{
    var response = default(TResponse);

    if (file != null && file.Length > 0)
    {
        var url = (!string.IsNullOrWhiteSpace(uri) ? new Uri(new Uri(this.baseUrl), uri) : new Uri(this.baseUrl)).ToString();

        using (var http = new HttpClient())
        {
            var content = new StreamContent(file.OpenReadStream());
            content.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);

            var httpResponse = await http.PostAsync(url, content);
            response = JsonConvert.DeserializeObject<TResponse>(await httpResponse.Content.ReadAsStringAsync());
        }
    }

    return response;
}

API сторона:

[HttpPost]
public async Task<IActionResult> Post([FromRoute]string tourId)
{
    var response = new ApiResponse();
    if (Request.ContentType.Equals("image/jpeg"))
    {
        using (var stream = new MemoryStream())
        {
            await Request.Body.CopyToAsync(stream);

            ...
        }
    }
    else
    {
        ...
    }

    return Ok(response);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...