jackshooter
jackshooter

Reputation: 21

Flutter web: How to upload Image to ASP .NET Core web API?

I have an API which is for a CMS that can change my entire 'AboutUs' web page. The following was the Data Model that holds all the contents of that 'About Us' webpage.

I have a Future method that goes this way to update the 'About Us' webpage contents within database only. (via ASP.Net Core web API)

Future updateAboutUsContent() async

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

And This Future will call the ASP web API which is as below:

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

Unfortunately when I run the code, the following errors only shows:

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

I used breakpoints to examine each of the lines to track where is the null (in the Flutter's updateAboutUsContent() Future), which this line

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

and this line,

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

The 'PlatformFile imageFile' receives the imageFile. It shows the filename, bytes,... all those in the VS Code 'Run and Debug'.

The 'PlatformFile imageFile' still gets the image file, but until this line

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

it still execute this line but after that the aforementioned TypeError shows. It seems that, MAYBE

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

↑↑something here was wrong.↑↑

Referring to the first two codes I pasted at first, i.e. updateAboutUsContent(), and PutAboutUsContent() located in the Web API, Can I know

  1. Where did I done wrong?
  2. How can I correct my 'updateAboutUsContent()' method, so that it can connect and 'PUT' data to the 'PutAboutUsContent()' in the .Net Core web API?
  3. How can I convert Flutter's 'PlatformFile' to ASP's 'IFormFile' so that it binds to the corresponding argument that accepts the imageFile?

I'm very new to Flutter web, ASP.NET Core 5.0 web API, and really don't have any idea/concepts about how to upload images to the ASP.NET from flutter web, so something in my updateAboutUsContent() in the Flutter may wrote wrong. I have tested the PutAboutUsContent() situated in the Web API using Swagger UI, nothing there was wrong, and I was prohibited to change the codes there, I just can use it.

So, I plead. Is there someone could lend a hand, please?

UPDATE

Now the Future updateAboutUsContent() async is changed to this:

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

And the Future method can properly go until to the if(statusCode ==??) there, however, it still returns 400. Now, the console appears:

==================
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***)

When the first two lines of the updateAboutUsContent() method was executed, ie.

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

The following shows: Breakpoints Operations to observe null where the readStream returns: Instance of '_ControllerStream<List>'. So I think this was the reason statusCode 400 was returned.

So, how should I solve it? Or if I wrongly identified the issue causing the error, then where can I improved?

Upvotes: 1

Views: 1672

Answers (1)

Eugene Kuzmenko
Eugene Kuzmenko

Reputation: 1507

You have to send one request with all data.

Something like this:

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

Also, check this answer .

Upvotes: 1

Related Questions