首页 > 解决方案 > Flutter web:如何将图像上传到 ASP .NET Core Web API?

问题描述

我有一个用于 CMS 的 API,它可以更改我的整个“AboutUs”网页。以下是包含“关于我们”网页的所有内容的数据模型。

我有一个 Future 方法,它可以仅更新数据库中的“关于我们”网页内容。(通过 ASP.Net Core Web API)

未来 updateAboutUsContent() 异步

 Future<AboutUsContextModel> updateAboutUsContent(
  String title,
  String context,
  String subcontext,
  PlatformFile imageId,
) async {
  final abUC =
      await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
          headers: <String, String>{
            "Content-Type": "multipart/form-data;charset=UTF-8",
          },
          body: jsonEncode(<String, dynamic>{
            "title": title,
            "context": context,
            "subContext": subcontext
          }));
  final request = http.MultipartRequest(
      "POST", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
  request.files.add(http.MultipartFile(
      "imageFile",
      imageId.readStream,
      imageId.size,
      filename: imageId.name.split("/").last);
  
    var resp = await request.send();
    String response = await resp.stream.bytesToString();
    print("==================\nThis is the response:\n=================" +
        response);
  }

  if (abUC.statusCode == 200) {
    //If the server did return a 200 Created All green post
    //then parse the JSON
    return AboutUsContextModel.fromJson(jsonDecode(abUC.body));
  } else if (abUC.statusCode == 400) {
    throw Exception('Error code was 400. About Us Content Not Foun');
  } else {
    throw Exception('Nothing About Us Content created in Flutter');
  }
}

This Future 将调用 ASP Web API,如下所示:

[HttpPut("{id}")]
        public async Task<ActionResult<AboutUsContextModel>> PutAboutUsContent(int id, string title, string context, string subcontext, IFormFile imageFile)
        {
            AboutUsContextModel abUC = await _context.AboutUsContextModel.Include(lim => lim.Image).FirstOrDefaultAsync(limy => limy.AboutUs_Context_Id == id);
            if(abUC == null)
            {
                return BadRequest("No such About Us Content!");
            }
            if(imageFile != null)
            {
                ImageModel imgfrmDB = abUC.Image;
                if(imgfrmDB != null)
                {
                    string cloudDomaim = "https://privacy-web.conveyor.cloud";
                    string uploadDrcty = _webEnvr.WebRootPath + "\\Images\\";

                    if (!Directory.Exists(uploadDrcty))
                    {
                        Directory.CreateDirectory(uploadDrcty);
                    }
                    string filePath = uploadDrcty + imageFile.FileName;

                    using (var fileStream = new FileStream(filePath, FileMode.Create))
                    {
                        await imageFile.CopyToAsync(fileStream);
                        await fileStream.FlushAsync();
                    }
                    using (var memoryStream = new MemoryStream())
                    {
                        await imageFile.CopyToAsync(memoryStream);
                        imgfrmDB.Image_Byte = memoryStream.ToArray();
                    }
                    imgfrmDB.ImagePath = cloudDomaim + "/Images/" + imageFile.FileName;
                    imgfrmDB.Modify_By = "CMS Admin";
                    imgfrmDB.Modity_dt = DateTime.Now;


                }
            }
            abUC.Title = title;
            abUC.Context = context;
            abUC.SubContext = subcontext;
            abUC.Modify_By = "CMS Admin";
            abUC.Modity_dt = DateTime.Now;

            _context.Entry(abUC).State = EntityState.Modified;
            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!AboutUsContextModelExists(abUC.AboutUs_Context_Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return Ok("About Us Content Delivered, Edit Successful");
        }

不幸的是,当我运行代码时,仅显示以下错误:

TypeError: Cannot read properties of null (reading 'listen')
    at byte_stream.ByteStream.new.listen (http://localhost:60452/dart_sdk.js:36349:31)
    at _StreamControllerAddStreamState.new._AddStreamState.new (http://localhost:60452/dart_sdk.js:37160:37)
    at new _StreamControllerAddStreamState.new (http://localhost:60452/dart_sdk.js:37191:53)
    at _AsyncStreamController.new.addStream (http://localhost:60452/dart_sdk.js:36687:24)
    at _AsyncStarImpl.new.addStream (http://localhost:60452/dart_sdk.js:33462:46)
at multipart_request.MultipartRequest.new._finalize (http://localhost:60452/packages/http/src/multipart_request.dart.lib.js:352:22)
    at _finalize.next (<anonymous>)
    at _AsyncStarImpl.new.runBody (http://localhost:60452/dart_sdk.js:33416:40)
    at Object._microtaskLoop (http://localhost:60452/dart_sdk.js:40808:13)
    at _startMicrotaskLoop (http://localhost:60452/dart_sdk.js:40814:13)
    at http://localhost:60452/dart_sdk.js:36279:9

我使用断点检查每一行以跟踪 null 的位置(在 Flutter 的 updateAboutUsContent() Future 中),这一行

final abUC =
      await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
          headers: <String, String>{
            "Content-Type": "multipart/form-data;charset=UTF-8",
          },
          body: jsonEncode(<String, dynamic>{
            "title": title,
            "context": context,
            "subContext": subcontext,
            // 'imageFile': imageId
          })); 

而这条线,

final request = http.MultipartRequest(
      "POST", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));

'PlatformFile imageFile' 接收 imageFile。它显示了文件名、字节、......所有在 VS Code 'Run and Debug' 中的内容。

'PlatformFile imageFile' 仍然获取图像文件,但直到这一行

 request.files.add(http.MultipartFile(
              "imageFile",
              imageId.readStream,
              imageId.size,
              filename: imageId.name.split("/").last);

它仍然执行这一行,但之后上述 TypeError 显示。看来,也许

http.MultipartFile(
              "imageFile",
              imageId.readStream,
              imageId.size,
              filename: imageId.name.split("/").last)

↑↑这里出了点问题。↑↑

参考我最开始粘贴的前两个代码,即updateAboutUsContent()和位于Web API中的PutAboutUsContent(),我知道吗

  1. 我哪里做错了?
  2. 如何更正我的“updateAboutUsContent()”方法,以便它可以将数据连接并“PUT”到 .Net Core Web API 中的“PutAboutUsContent()”?
  3. 如何将 Flutter 的“PlatformFile”转换为 ASP 的“IFormFile”,以便绑定到接受 imageFile 的相应参数?

我对 Flutter Web、ASP.NET Core 5.0 Web API 非常陌生,并且对于如何从 Flutter Web 将图像上传到 ASP.NET 真的没有任何想法/概念,所以在我的 updateAboutUsContent() 中Flutter 可能写错了。我已经使用 Swagger UI 测试了位于 Web API 中的 PutAboutUsContent(),没有任何问题,并且我被禁止更改那里的代码,我只能使用它。

所以,我恳求。请问有人可以帮忙吗?

更新

现在 Future updateAboutUsContent() async 更改为:

Future<AboutUsContextModel> putAboutUsContent(
  String title,
  String context,
  String subcontext,
  PlatformFile imageId,
) async {
  final abUC =
      await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
          headers: <String, String>{
            "Content-Type": "multipart/form-data;charset=UTF-8",
          },
          body: jsonEncode(<String, dynamic>{
            "title": title,
            "context": context,
            "subContext": subcontext,
            // 'imageFile': imageId
          }));
  final request = http.MultipartRequest(
      "PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
  var fileadded =
      new http.MultipartFile("imageFile",
 imageId.readStream,
 imageId.size,
          filename: imageId.name);
  if (fileadded != null) {
    request.headers
        .addAll(<String, String>{"Content-Type": "multipart/form-data"});
    request.files.add(fileadded);
    var resp = await request.send();
    String response = await resp.stream.bytesToString();
    print("==================\nThis is the response:\n=================" +
        response);
  }

  if (abUC.statusCode == 200) {
    //If the server did return a 200 Created All green post
    //then parse the JSON
    return AboutUsContextModel.fromJson(jsonDecode(abUC.body));
  } else if (abUC.statusCode == 400) {
    throw Exception('Error code was 400. About Us Content Not Foun');
  } else {
    throw Exception('Nothing About Us Content created in Flutter');
  }
}

并且 Future 方法可以正常运行,直到那里的 if(statusCode ==??),但是,它仍然返回 400。现在,控制台出现:

==================
This is the response:
=================Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
 ---> Npgsql.PostgresException (0x80004005): 23502: null value in column "Title" of relation "AboutUs_Context" violates not-null constraint
   at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
  Exception data:
    Severity: ERROR
    SqlState: 23502
    MessageText: null value in column "Title" of relation "AboutUs_Context" violates not-null constraint
    Detail: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
    SchemaName: WebContext
    TableName: AboutUs_Context
    ColumnName: Title
    File: d:\pginstaller_13.auto\postgres.windows-x64\src\backend\executor\execmain.c
    Line: 1953
    Routine: ExecConstraints
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Privacy_Web.Controllers.AboutUsContextModelsController.PutAboutUsContent(Int32 id, String title, String context, String subcontext, IFormFile imageFile) in D:\distributor_dashboard_v2\Privacy_Web\privacy_web_backend\Privacy_Web\Controllers\AboutUsContextModelsController.cs:line 224
   at lambda_method299(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Length: 263429
Content-Type: multipart/form-data; boundary=dart-http-boundary-zMVNRV2ehRqP4TYkdPpFn.dOrsckK2tfoxBV_s6z5coua9ye0+m
Host: localhost:44395
Referer: http://localhost:63925/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
origin: http://localhost:63925
sec-fetch-site: cross-site
sec-fetch-mode: cors
sec-fetch-dest: empty
x-server-sni: haproxy.privacy-web-kb5.conveyor.cloud
x-forwarded-for: (***IP address here, censored to be seen***)

当执行 updateAboutUsContent() 方法的前两行时,即。

final abUC =
      await http.put(Uri.parse(urlAddress + "/api/AboutUsContextModels/2"),
          headers: <String, String>{
            "Content-Type": "multipart/form-data;charset=UTF-8",
          },
          body: jsonEncode(<String, dynamic>{
            "title": title,
            "context": context,
            "subContext": subcontext,
            // 'imageFile': imageId
          }));
  final request = http.MultipartRequest(
      "PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));

以下显示: 断点操作来观察 null readStream 返回的位置: '_ControllerStream<List>' 的实例。所以我认为这就是返回 statusCode 400 的原因。

那么,我该如何解决呢?或者,如果我错误地确定了导致错误的问题,那么我可以在哪里改进?

标签: asp.net-coreflutter-web

解决方案


您必须发送一个包含所有数据的请求。

像这样的东西:

Future<AboutUsContextModel> putAboutUsContent(
  String title,
  String context,
  String subcontext,
  PlatformFile imageId,
) async {
    var request = http.MultipartRequest("PUT", Uri.parse(urlAddress + "/api/AboutUsContextModels/2"));
    request.fields['title'] = title;
    request.fields['context'] = context;
    request.fields['subContext'] = subContext;
    request.files.add(http.MultipartFile(
        'imageFile',
        imageId.readStream,
        imageId.size,
        filename: imageId.name,
    ));

     final resp = await request.send();


    if (resp.statusCode == 200) {
      //If the server did return a 200 Created All green post
      //then parse the JSON
      return AboutUsContextModel.fromJson(jsonDecode(resp.body));
    } else if (resp.statusCode == 400) {
      throw Exception('Error code was 400. About Us Content Not Foun');
    } else {
      throw Exception('Nothing About Us Content created in Flutter');
    }
}

另外,检查这个答案


推荐阅读