c# - AWS EC2 MVC 应用程序和 Lambda Serverless Web API 应用程序之间的文件传输导致数据损坏

标签 c# asp.net-mvc amazon-web-services asp.net-web-api imagemagick

我有一个 MVC 应用程序,托管在 Windows Server 2016 AWS EC2 实例上。该应用程序是一个管理工具。此应用程序使用作为 AWS Lambda 无服务器应用程序托管的 Web API 应用程序 ( https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/lambda-build-test-severless-app.html )。

我的 MVC 应用程序的一个区域允许用户使用表单文件输入上传图像。然后,该文件被发送回 MVC Controller ,并发送到 API 实用程序,该实用程序将文件发送到 API。然后,API 调整大小(使用 Magick.NET)并将图像保存到 S3 存储桶,并将生成的 URL 保存到 MySQL 数据库。

当在我的机器上本地运行时,这一切都完美运行。问题是当我尝试在实时网站上上传图像时。结果是,当图像数据加载到 MagickImage 中时,出现以下错误:

ImageMagick.MagickCorruptImageErrorException: Not a JPEG file: starts with 0xef 0xbf `' @ error/jpeg.c/JPEGErrorHandler/332\n

我添加了一些代码,以在 MVC 应用程序(在将文件发布到 API 之前)和收到文件后的 API 中记录数据的前 20 个字节(这是一个字节数组)。我发现我收到的值完全不同,如下所示:

  • 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 Controller :

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 实用程序(在 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 Controller :

[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 }))
    {
        ...
    }
}

最佳答案

问题原来是我的 AWS API Gateway 不接受二进制数据。默认情况下,API Gateway 将消息正文视为 JSON,如此处所述 - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html

By default, API Gateway treats the message body as a text payload and applies any preconfigured mapping template to transform the JSON string.

我相信这是腐败的根源。为了解决这个问题,我必须在 API Gateway 中添加“image/jpeg”作为可接受的二进制媒体类型,如下所示:

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

关于c# - AWS EC2 MVC 应用程序和 Lambda Serverless Web API 应用程序之间的文件传输导致数据损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52173871/

相关文章:

c# - 用户控件中的图像未显示在窗口中

c# - 将 UTF8 字符串保存到 SQL Server

java - 用于存储图像的 AWS S3 和 Cloudfront

mysql - 我可以在单个 Amazon RDS 实例上创建多少个数据库

amazon-web-services - kops - 得到错误的 kubectl 上下文

c# - 我已经使用 LINQ 将 Excel 文件加载到 .NET 中,现在如何将数据批量插入到 DB (oracle) 表中?

c# - 使用 TFS api 执行(通过/失败)测试用例?

c# - 如何从 Azure API 应用程序解决方案生成 swagger 元数据文件?

css - 为什么我的 css 类没有在初始加载时触发?

c# - ASP.net Identity 2.0 注销另一个用户