Send image with HttpClient to POST WebApi and cons

2020-05-01 07:28发布

问题:

My HttpClient sends the image with PostAsync. I am not really sure what to do now since this is my first REST Api and I can't really adapt the things I sorted out in other posts yet. Hopefully you guys can help me out and can give me a direction.

public async Task SendImage(string fullFileName, string fileName)
    {            
        var client = new HttpClient();
        client.BaseAddress = new Uri("http://x.x.x.x");

        var content = new StringContent(fullFileName, Encoding.UTF8, "image/jpg");
        HttpResponseMessage response = await client.PostAsync($"/values/file/{fileName}", content);
    }

I have several questions about the POST function. First of all I can successfully access it with PostMan and the fileName is correct.

How do I read the image data and write it to a file?

[HttpPost("file/{fileName}")]
public void Upload(string fileName)
{        
    Debug.Write(fileName);
}

EDIT:

I setup my environment and I can now send a post via the internet to my published Web Api. On my app just nothing happens. For now I just tried to get something of a message to work on but I dont getting one.

[HttpPost("file/{fileName}")]
public HttpResponseMessage Upload(UploadedFile fileName)
{
    Debug.Write(fileName);
    if (!ModelState.IsValid)
    {

    }
    if (fileName == null)
    {

    }

    string destinationPath = Path.Combine(@"C:\", fileName.FileFullName);
    System.IO.File.WriteAllBytes(destinationPath, fileName.Data);
    HttpResponseMessage rm = new HttpResponseMessage();
    rm.StatusCode = HttpStatusCode.OK;
    return rm;
}

回答1:

You're going fine until you're trying to retrieve the image data, I'm afraid.

According to your question:

How do I read the image data and write it to a file?

All you want to do is getting the file's data and its file name and sending it to your service.

I would personally create an UploadedFile class on both sides (client and service side), having the file's name and its data, so:

public class UploadedFile
{
    public string FileFullName { get; set; }
    public byte[] Data { get; set; }

    public UploadedFile(string filePath)
    {
        FileFullName = Path.GetFileName(Normalize(filePath));
        Data = File.ReadAllBytes(filePath);
    }

    private string Normalize(string input)
    {
        return new string(input
                .Normalize(System.Text.NormalizationForm.FormD)
                .Replace(" ", string.Empty)
                .ToCharArray()
                .Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
                .ToArray());
    }
}

Then you will need, for example, the NewtonSoft's JsonConvert in order to serialize the object and send it through.

So now you would be able to send your data async:

public async Task SendDataAsync(string fullFilePath)
{
    if (!File.Exists(fullFilePath))
        throw new FileNotFoundException();
    var data = JsonConvert.SerializeObject(new UploadedFile(fullFilePath));
    using (var client = new WebClient())
    {
        client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
        await client.UploadStringTaskAsync(new Uri("http://localhost:64204/api/upload/"), "POST", data);
    }
}

Now, make sure you correctly handle the request on the server side. If for whatever reason the parameters doesn't match it won't enter into the method (remember having the same model/class - UploadedFile on the service as well).

On the service, just change the arguments of your method and perform something "like this":

[HttpPost]
public HttpResponseMessage Upload(UploadedFile file)
{
    if (!ModelState.IsValid)
        ...

    if (file == null)
        ...
    string destinationPath = Path.Combine(_whateverPath, file.FileFullName);
    File.WriteAllBytes(destinationPath, file.Data);
}

Hope it helped you having an idea about what to do and what you're actually doing wrong. I've exposed something similar based in my experience.

EDIT: I've actually uploaded an example with both sides working: a simple .NET Core console app which retrieves a file and sends it through POST and a basic WebAPI2 service with a simple controller to retrieve the data. Both ready to go and tested working! Download it here.

Enjoy.



回答2:

1.Your controller should look like this:

//For .net core 2.1
[HttpPost]
public IActionResult Index(List<IFormFile> files)
{
    //Do something with the files here. 
    return Ok();
}

//For previous versions

[HttpPost]
public IActionResult Index()
{
    var files = Request.Form.Files;
    //Do something with the files here. 
    return Ok();
}

2.To upload a file you can also use Multipart content:

        public async Task UploadImageAsync(Stream image, string fileName)
        {
            HttpContent fileStreamContent = new StreamContent(image);
            fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
            fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
            using (var client = new HttpClient())
            using (var formData = new MultipartFormDataContent())
            {
                formData.Add(fileStreamContent);
                var response = await client.PostAsync(url, formData);
                return response.IsSuccessStatusCode;
            }
         }

3.If you are uploading large files you should consider streaming the files instead, you can read about it here