Xamarin Forms c# - Jak wysłać obraz poprzez net core API i zapisać?

0

Witam serdecznie. Stworzyłem następującą akcję API. Ma ona za zadanie pobrać obraz, zapisać go na dysku i utworzyć odpowiedni wpis w bazie danych:

public class UploadDamageImageModel
    {
        public Guid DamageTicketId { get; set; }
    }
[HttpPost]
        [Route("Tickets/UploadImage")]
        [Authorize(AuthenticationSchemes = "Bearer")]
        public async Task<IActionResult> UploadDamageImage([FromBody] UploadDamageImageModel Model)
        {
            string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            ApplicationUser user = await _userManager.FindByIdAsync(userId);
            if (user == null)
                return Unauthorized();

            var foundedTicket = await _dbContext.DamageTickets.FirstOrDefaultAsync(x => !x.IsClosed && x.ApplicationUserId == user.Id && x.Id == Model.DamageTicketId);
            if (foundedTicket == null)
                return NotFound();

            try
            {
                if(Request.Form.Files == null || Request.Form.Files.Count() == 0)
                    return BadRequest();
                var file = Request.Form.Files[0];

                string wwwPath = _hostingEnvironment.WebRootPath;
                string contentPath = _hostingEnvironment.ContentRootPath;
                string path = Path.Combine(wwwPath, "Images", "VehicleDamageImages");
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                if (file.Length == 0)
                    return BadRequest();

                string fileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(file.FileName);
                using (FileStream stream = new FileStream(Path.Combine(path, fileName), FileMode.Create))
                {
                    await file.CopyToAsync(stream);
                }

                VehicleDamageImage vehicleDamageImage = new VehicleDamageImage()
                {
                    Id = Guid.NewGuid(),
                    DamageTicketId = foundedTicket.Id,
                    ImageName = fileName
                };
                await _dbContext.VehicleDamageImages.AddAsync(vehicleDamageImage);
                await _dbContext.SaveChangesAsync();

                return Ok(new Response());
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex}");
            }
        }

Następne co chciałbym zrobić:
Użytkownik w aplikacji xamarin-forms robi zdjęcie i chciałbym za pomocą API przesłać je na serwer. Stworzyłem metodę w serwisie:

public async Task UploadVehicleDamageImage(LoginResponse loginResponse, Guid DamageTicketId, byte[] picture)
        {
            Debug.WriteLine("VehicleService - UploadVehicleDamageImage");

            MultipartFormDataContent content = new MultipartFormDataContent();
            content.Add(new ByteArrayContent(picture, 0, picture.Count()), "VehicleDamageImage", "VehicleDamageImage.jpg");

            HttpClient httpClient = new HttpClient();
            HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl.Host}/api/v1/MobileAppApi/Tickets/UploadImage");
            httpRequestMessage.Content = content;
            httpRequestMessage.Headers.Add("Authorization", $"Bearer {loginResponse.token}");
            var response = await httpClient.SendAsync(httpRequestMessage);
            string responseString = await response.Content.ReadAsStringAsync();
            Debug.WriteLine(responseString);
            if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                throw new UnauthorizedAccessException("Token autoryzacji wygasł");
            }
            else
            if (response.IsSuccessStatusCode)
            {
                ResultModel result = JsonConvert.DeserializeObject<ResultModel>(responseString);
                if (!result.success)
                {
                    throw new Exception(result.error);
                }
                else
                {
                    
                }
            }
            else
            {
                throw new Exception($"Wystąpił błąd podczas wysyłania żądania ({response.StatusCode.ToString()})");
            }
        }

Czy ktoś może mi podpowiedzieć jak to poprawnie zrobić? Brakuje mi pomysłów...

1

Na początek napisz z czym masz problem.

0

Oprócz obrazka muszę przesłać jeszcze identyfikator ticketu. Normalnie to robiłem stringcontent a teraz lekko zgłupiałem

1

I co cię powstrzymuje żeby wysłać te id jako np query parameter, albo w jakimś customowym headerze?

0

Pierwszy raz w życiu przesyłam obraz poprzez api i chcę to zrobić zgodnie ze sztuką :) Faktycznie, mogę dodać QueryParameter.
Czy w akcji API da się jakoś wykorzystać IFormFile? Cały czas w sumie celowałem w tym kierunku i potrzebuję potwierdzenia, że będzie to "ładniejsze" rozwiązanie.
Za jakieś dwie godziny przysiądę do tego tematu.

0

Okej:
A więc lekko zmodernizowałem akcję kontrolera i metodę w aplikacji mobilnej. Identyfikator otrzymuję poprawny, ale niestety zwraca mi BadRequest przy sprawdzaniu, czy plik istnieje. Co mogę robić nie tak?

[HttpPost]
        [Route("Tickets/UploadImage")]
        [Authorize(AuthenticationSchemes = "Bearer")]
        public async Task<IActionResult> UploadDamageImage([FromForm] UploadDamageImageModel Model)
        {
            string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            ApplicationUser user = await _userManager.FindByIdAsync(userId);
            if (user == null)
                return Unauthorized();

            var foundedTicket = await _dbContext.DamageTickets.FirstOrDefaultAsync(x => !x.IsClosed && x.ApplicationUserId == user.Id && x.Id == Model.DamageTicketId);
            if (foundedTicket == null)
                return NotFound();

            try
            {
                if(Request.Form.Files == null || Request.Form.Files.Count() == 0)
                    return BadRequest();
                var file = Request.Form.Files[0];

                string wwwPath = _hostingEnvironment.WebRootPath;
                string contentPath = _hostingEnvironment.ContentRootPath;
                string path = Path.Combine(wwwPath, "Images", "VehicleDamageImages");
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                if (file.Length == 0)
                    return BadRequest();

                string fileName = Guid.NewGuid().ToString() + System.IO.Path.GetExtension(file.FileName);
                using (FileStream stream = new FileStream(Path.Combine(path, fileName), FileMode.Create))
                {
                    await file.CopyToAsync(stream);
                }

                VehicleDamageImage vehicleDamageImage = new VehicleDamageImage()
                {
                    Id = Guid.NewGuid(),
                    DamageTicketId = foundedTicket.Id,
                    ImageName = fileName
                };
                await _dbContext.VehicleDamageImages.AddAsync(vehicleDamageImage);
                await _dbContext.SaveChangesAsync();

                return Ok(new Response());
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Internal server error: {ex}");
            }
        }

I w aplikacji mobilnej:

await vehicleService.UploadVehicleDamageImage(StaticData.CurrentUser, selectedTicket.Id, Converters.StreamToByteArray(image));
public async Task UploadVehicleDamageImage(LoginResponse loginResponse, Guid DamageTicketId, byte[] picture)
        {
            Debug.WriteLine("VehicleService - UploadVehicleDamageImage");

            MultipartFormDataContent content = new MultipartFormDataContent();
            content.Add(new ByteArrayContent(picture, 0, picture.Count()), "VehicleDamageImage");
            content.Add(new StringContent(DamageTicketId.ToString()), "DamageTicketId");

            HttpClient httpClient = new HttpClient();
            HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl.Host}/api/v1/MobileAppApi/Tickets/UploadImage");
            httpRequestMessage.Content = content;
            httpRequestMessage.Headers.Add("Authorization", $"Bearer {loginResponse.token}");
            var response = await httpClient.SendAsync(httpRequestMessage);
            string responseString = await response.Content.ReadAsStringAsync();
            Debug.WriteLine(responseString);
            if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                throw new UnauthorizedAccessException("Token autoryzacji wygasł");
            }
            else
            if (response.IsSuccessStatusCode)
            {
                ResultModel result = JsonConvert.DeserializeObject<ResultModel>(responseString);
                if (!result.success)
                {
                    throw new Exception(result.error);
                }
                else
                {
                    
                }
            }
            else
            {
                throw new Exception($"Wystąpił błąd podczas wysyłania żądania ({response.StatusCode.ToString()})");
            }
        }
1

Przetestuj sobie api najpierw postmanem. Jak będzie działać to rusz xamarina.
Ja robiłam nie tak dawno upload to posiłkowalem się https://docs.microsoft.com/en-au/aspnet/core/mvc/models/file-uploads?view=aspnetcore-6.0#storage-scenarios

Ale robiłem to przez Stream a nie IFormFile.

0

Okej, z tą akcją postman działa idealnie. Niestety gdy próbuję to przełożyć na xamarina to niby nie zgłasza mi błędu, ale nie dodaje również pliku:
Akcja API: https://pastebin.com/D81jCxTR
Serwis xamarin: https://pastebin.com/A7PDTfbF

Dodam, że wychodzi na to, że cała akcja kontrolera nie zgłasza żadnych błędów

EDIT:
Wszystko działa. Posiłkowałem się tym, że tutaj folder był cały czas pusty:
screenshot-20220410155205.png

Zapomniałem, że apka korzysta z opublikowanego kodu na iis express. Po sprawdzeniu ścieżki wszystko jest pięknie zrobione <3

[0:] {"response":"C:\\inetpub\\wwwroot\\MobileAppAPI\\wwwroot\\Images\\VehicleDamageImages\\284b47fb-e578-4375-9529-1cf40d5076b0.jpg","success":true,"error":null}

1 użytkowników online, w tym zalogowanych: 0, gości: 1