asp.net-core - 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(),我知道吗
- 我哪里做错了?
- 如何更正我的“updateAboutUsContent()”方法,以便它可以将数据连接并“PUT”到 .Net Core Web API 中的“PutAboutUsContent()”?
- 如何将 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"));
以下显示: readStream 返回的位置: '_ControllerStream<List>' 的实例。所以我认为这就是返回 statusCode 400 的原因。
那么,我该如何解决呢?或者,如果我错误地确定了导致错误的问题,那么我可以在哪里改进?
解决方案
您必须发送一个包含所有数据的请求。
像这样的东西:
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');
}
}
另外,检查这个答案。
推荐阅读
- c# - 当我点击逃生时,暂停菜单不会激活
- python - 如何在字典 python 中使用 */+- 等运算符和字符串?
- javascript - Select2 多个值(标签)
- java - 如何解决有关环境变量的启动 PyCharm 的错误不指向有效的 JVM 安装?
- android - Editext 出现时隐藏在键盘后面
- laravel - “.../storage/logs”中没有现有目录,无法创建:只读文件系统
- javascript - 我如何对本机清理功能做出反应?
- c++ - 如何显示我输入到文本文件中的所有数据?
- android - 将数据从 StatelessWidget 传递到 StatefulWidget
- javascript - 如何在 JavaScript 中删除撇号?