Request with file to web API - c#

I have a problem with the application sending the file to the web service. This is my endpoint/controller.
[HttpPost]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = "C:\\Files\\TEST.pdf";
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
This controller works fine in Postman.
and this is my application that makes the request:
byte[] bytes = System.IO.File.ReadAllBytes("C:\\Files\\files.pdf");
Stream fileStream = File.OpenRead("C:\\Files\\files.pdf");
HttpContent bytesContent = new ByteArrayContent(bytes);
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(bytesContent,"file", "files.pdf");
try
{
var response = await client.PostAsync(url, formData);
}catch(Exception ex)
{
Console.WriteLine(ex);
}
It doesn't work. I'm not receiving the file in controller. I also tried this:
string fileToUpload = "C:\\Files\\files.pdf";
using (var client = new WebClient())
{
byte[] result = client.UploadFile(url, fileToUpload);
string responseAsString = Encoding.Default.GetString(result);
}
but the result is the same. Would you be able to help?

Update 15/09/2020
This is the upload codes in ConsoleApplication. And it works with small file but not large file.
public static async Task upload(string url)
{
//const string url = "https://localhost:44308/file/post";
const string filePath = "C:\\Files\\files.pdf";
try {
using (var httpClient = new HttpClient{
Timeout = TimeSpan.FromSeconds(3600)
})
{
using (var form = new MultipartFormDataContent())
{
using (var fs = System.IO.File.OpenRead(filePath))
{
fs.Position = 0;
using (var streamContent = new StreamContent(fs))
{
form.Add(streamContent, "files", Path.GetFileName(filePath));
HttpResponseMessage response = httpClient.PostAsync(url, form).Result;
fs.Close();
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
There are two steps to fix your problem.
1.Add ContentType to Headers
bytesContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
2. The file parameter name in formData should match action parameter name.
formData.Add(bytesContent,"file", "files.pdf"); //should be files
public async Task<IActionResult> Post(List<IFormFile> files)
Update
HttpClient.PostAsync() doesn't work when awaited in Console Application. Instead of blocking with .Result, use .GetAwaiter().GetResult().
HttpResponseMessage response = httpClient.PostAsync(url, form).Result;
Here is the code to show how to upload file.
Codes of Controller
public class FileController : Controller
{
[HttpPost]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = "C:\\Files\\TEST.pdf";
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
return Ok();
}
[HttpGet]
public async Task<IActionResult> upload()
{
const string url = "https://localhost:44308/file/post";
const string filePath = #"C:\\Files\\files.pdf";
using (var httpClient = new HttpClient())
{
using (var form = new MultipartFormDataContent())
{
using (var fs = System.IO.File.OpenRead(filePath))
{
using (var streamContent = new StreamContent(fs))
{
using (var fileContent = new ByteArrayContent(await streamContent.ReadAsByteArrayAsync()))
{
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// "file" parameter name should be the same as the server side input parameter name
form.Add(fileContent, "files", Path.GetFileName(filePath));
HttpResponseMessage response = await httpClient.PostAsync(url, form);
}
}
}
}
}
return Ok();
}
}
Test

Related

Is there a way to copy a file to temp folder and then get the file itself not just the file name?

I am uploading multiple files through a form and then iterating through each file and am creating a zero byte temp file and getting the file name using the GetTempFileName. Is there a way to copy the file itself not just the file name and the zero byte?. The code runs but i always get the zero byte file on the other end of my API which should happen. My code is like below.
public async Task<IActionResult> UploadDocumentsAsync(Documents files)
{
try
{
List<string> tempFilePath = new List<string>();
List<string> fileName = new List<string>();
foreach (var doc in files.documents)
{
tempFilePath.Add(Path.GetTempFileName());
fileName.Add(doc.FileName);
}
MultipartFormDataContent formData = new MultipartFormDataContent();
var filePath = tempFilePath;
string token = Token();
List<FileStream> streams = new List<FileStream>();
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
for (int i = 0; i < tempFilePath.Count; i++)
{
var fileStream = new FileStream(tempFilePath[i], FileMode.Open);
formData.Add(new StreamContent(fileStream), fileName[i], fileName[i]);
streams.Add(fileStream);
}
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(_configuration["Application:AppDomain"]) + "api/Document")
{
Content = formData
};
request.Headers.Add("accept", "application/json");
var response = await client.SendAsync(request);
streams.ForEach(stream =>
{
stream.Dispose();
});
if (response.IsSuccessStatusCode)
{
//Handle success
}
//Handle failure
}
}
}
catch (Exception e)
{
//Handle the exception
}
}
You need to save the actual file to temp fileName if you want to access it later
public async Task<IActionResult> UploadDocumentsAsync(Documents files)
{
try
{
List<string> tempFilePath = new List<string>();
List<string> fileName = new List<string>();
foreach (var doc in files.documents)
{
string fileTempFileName = Path.GetTempFileName();
tempFilePath.Add(fileTempFileName );
fileName.Add(doc.FileName);
using(Stream outStream = File.OpenWrite(Path.Combine(Path.GetTempPath(), fileTempFilePath )))
{
doc.CopyTo(outStream);
}
}
//Continue your code [...]
}
catch (Exception e)
{
//Handle the exception
}
}

Response 400 on the post of httpclient in dotnet core

I am wrote the code to POST from my web application but it is sending 400 bad request.
See the my controller class:
[HttpPost]
public async Task<IActionResult> CreateAgent(AgentCreateDto agentCreateDto)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var responseTask = client.PostAsJsonAsync("Agent/CreateAgent", agentCreateDto);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var newAgentAdd = JsonConvert.DeserializeObject<AgentCreateDto>(await result.Content.ReadAsStringAsync());
var newAgent = newAgentAdd;
TempData["SuccessMessage"] = $"Agent {newAgent.FirstName} was successfully created.";
return RedirectToAction("AgentLists");
//return RedirectToAction("GetAgentById", new { AgentId = newAgent.usersId});
}
}
return View();
}
Here is below the problem image please see for the reference.
Here is api code for that I am send the request:
[HttpPost("CreateAgent")]
public async Task<IActionResult> CreateAgent([FromForm]AgentCreateDto agentCreateDto)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid model object");
}
if (agentCreateDto.ProfileImage != null)
{
string uniqueFileName = UploadProfileImage(agentCreateDto.ProfileImage);
agentCreateDto.ProfileNewImage = uniqueFileName;
}
var createAgent = await _userService.CreateAgent(agentCreateDto);
//_logger.LogInformation($"User added to the for file test. ");
return Created("", new
{
Id = createAgent.UsersId,
Message = "Agent created Successfully",
Status = "Success",
UserType = createAgent.UserType
});
}
And for image uploading code at the API side:
private string UploadProfileImage(IFormFile ProfileImage)
{
string uniqueFileName = null;
if (ProfileImage != null)
{
string uploadsFolder = Path.Combine(_webHostEnvironment.ContentRootPath, "ProfileImage");
uniqueFileName = Guid.NewGuid().ToString() + "_" + ProfileImage.FileName;
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
ProfileImage.CopyTo(fileStream);
}
}
return uniqueFileName;
}
The correct way to post file and additional data by HttpClient like below:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(agentCreateDto.Id.ToString()), nameof(agentCreateDto.Id));
formContent.Add(new StringContent(agentCreateDto.Message), nameof(agentCreateDto.Message));
formContent.Add(new StringContent(agentCreateDto.UserType), nameof(agentCreateDto.UserType));
//other proerties....
formContent.Add(new StreamContent(agentCreateDto.ProfileImage.OpenReadStream()), nameof(agentCreateDto.ProfileImage), Path.GetFileName(agentCreateDto.ProfileImage.FileName));
//change PostAsJsonAsync to PostAsync.....
var responseTask = client.PostAsync("/Agent/CreateAgent", formContent);
responseTask.Wait();
var result = responseTask.Result;
//more code...
}

Download files Angular, .Net 5

I have a problem downloading files.
When I download the file, it turns out to be empty, I think that the problem is in the wrong headers, but I could not figure out what exactly.
I have a response body
from controller
But I have empty response arraybuffer
from component
And empty zip downloaded
What am I doing wrong?
request.component.ts
downloadZip() {
this.requestService.downloadZip(this.request.id)
.subscribe((res) => {
const blob = new Blob([res], { type: 'application/zip' });
saveAs(blob, 'Обращение №' + this.request.id + '.zip');
})
}
request.service.ts
downloadZip(requestId: number): Observable<any> {
return this.http.get(this.apiUrl + '/request/downloadZip?id=' + requestId, { responseType: 'arraybuffer'});
}
RequestController.cs
[HttpGet]
[Route("api/request/downloadZip")]
public async Task<IActionResult> DownloadZip(int id)
{
var stream = await _fileStoreService.GetRequestFilesZip(id);
Response.Body = stream;
Response.ContentType = "application/zip";
return Ok();
}
FileStoreService.cs
public async Task<MemoryStream> GetRequestFilesZip(int id)
{
var query = await _db.RequestMedia
.Where(_ => _.RequestId == id)
.ToListAsync();
var fileSharePath = Path.Combine(_configuration["FileTableRootPath"], "RequestFileStore");
var files = new List<string>();
foreach (var media in query)
files.Add(Path.Combine(fileSharePath, media.Hash + Path.GetExtension(WebUtility.HtmlDecode(media.Name))));
var memory = new MemoryStream();
ZipStrings.UseUnicode = true;
using(var zipStream = new ZipOutputStream(memory))
{
zipStream.SetLevel(3);
foreach(var file in files)
{
var entry = new ZipEntry(Path.GetFileName(file))
{
DateTime = DateTime.Now
};
zipStream.PutNextEntry(entry);
var inStream = new MemoryStream();
using var f = new FileStream(file, FileMode.Open, FileAccess.Read);
await f.CopyToAsync(inStream);
inStream.Close();
var content = inStream.ToArray();
await zipStream.WriteAsync(content.AsMemory(0, content.Length));
zipStream.CloseEntry();
}
zipStream.IsStreamOwner = false;
}
memory.Position = 0;
return memory;
}
I changed this
request.service.ts
downloadZip(requestId: number) {
return this.http.get(this.apiUrl + '/request/downloadZip?id=' + requestId, { responseType: 'blob'});
}
and this
RequestController.cs
[HttpGet]
[Route("api/request/downloadZip")]
public async Task<IActionResult> DownloadZip(int id)
{
var stream = await _fileStoreService.GetRequestFilesZip(id);
return new FileStreamResult(stream, "application/zip");
}
Everything works fine

Http client Post with body parameter and file in c#

I was trying to attach a csv file as a body parameter in my test script. But still as per the below code controller expect file and just curious how should I pass that.
I run test script in below order
Method-1
public void AttachedRatesFile(string fileName)
{
_form = string.IsNullOrWhiteSpace(fileName)
? _form = new StringContent(string.Empty)
: _form = new StreamContent(File.OpenRead($"{ResourceFolder}{fileName}"));
_form.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
_form.Headers.ContentDisposition = new ContentDispositionHeaderValue(fileName);
}
Method-2
public void PostRequestExecutes(string resource)
{
var content = new MultipartFormDataContent{_form};
WhenThePostRequestExecutesWithContent(resource, content);
}
Method-3
public async void WhenThePostRequestExecutesWithContent(string resource, HttpContent content)
{
ResponseMessage = await HttpClient.PostAsync(resource, content);
}
I see null in below file parameter
Controller:
public async Task<IActionResult> SeedData(IFormFile file)
{
var result = await _seedDataService.SeedData(file);
return Ok(new { IsUploadSuccesful = result});
}
I would add that to the body as a stream
var memoryContentStream = new MemoryStream();
using (var streamWriter = new StreamWriter(memoryContentStream, Encoding.UTF8, 1000,
true))
{
using (var jsonTextWriter = new JsonTextWriter(streamWriter))
{
var jsonSerializer = new JsonSerializer();
jsonSerializer.Serialize(jsonTextWriter, OBJECT);
jsonTextWriter.Flush();
}
}
if (memoryContentStream.CanSeek)
{
memoryContentStream.Seek(0, SeekOrigin.Begin);
}
Then
using (var streamContent = new StreamContent(memoryContentStream))
{
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = streamContent;
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
var stream = await response.Content.ReadAsStreamAsync();
response.EnsureIsSuccessStatusCode();
}
}
The above would first write the content as a memory stream and then when creating the POST request you can send the stream as a streamContent

How to pass the File Data from C# Console application to WebApi?

I am trying to call the Web api method for saving the File Data.When I debug Webapi method I found that ContentLength is not coming as correct, because of this when i am retrieving the file it is showing error as corrupted file.
My Class method is :-
using (var formData = new MultipartFormDataContent())
{
HttpContent stringContent = new StringContent(file);
formData.Add(stringContent, "file", file);
formData.Add(new StringContent(JsonConvert.SerializeObject(file.Length)), "ContentLength ");
HttpResponseMessage responseFile = client.PostAsync("Report/SaveFile?docId=" + docId, formData).Result;
}
My Web api method is :-
[HttpPost]
public HttpResponseMessage SaveFile(long docId)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Unauthorized);
try
{
var httpRequest = HttpContext.Current.Request;
bool IsSuccess = true;
if (httpRequest.Files.Count > 0)
{
var docfiles = new List<string>();
foreach (string file in httpRequest.Files)
{
HttpPostedFile postedFile = httpRequest.Files[file];
// Initialize the stream.
Stream myStream = postedFile.InputStream;
myStream.Position = 0;
myStream.Seek(0, SeekOrigin.Begin);
var _item = CorrectedReportLibrary.Services.ReportService.SaveFile(myStream,docId);
response = Request.CreateResponse<bool>((IsSuccess)
? HttpStatusCode.OK
: HttpStatusCode.NoContent,
IsSuccess);
}
}
}
catch (Exception ex)
{
Theranos.Common.Library.Util.LogManager.AddLog(ex, "Error in CorrectedReportAPI.Controllers.SaveDocument()", null);
return Request.CreateResponse<ReportDocumentResult>(HttpStatusCode.InternalServerError, null);
}
return response;
}
How can I set the ContentLength from C# class method?
It looks a bit strange that you use ContentLength as the second parameter on the StringContent class. It is suppose to be which encoding you want to use, for example
new StringContent(content, Encoding.UTF8). I don't think it is the content length that is the issue here.
StringContent class
I guess since it is a file you want to upload, you already have the file read as a stream, so I usually do something like this:
Client:
private async Task UploadFile(MemoryStream file)
{
var client = new HttpClient();
var content = new MultipartFormDataContent();
content.Add(new StreamContent(file));
var result = await client.PostAsync("Report/SaveFile?docId=" + docId, content);
}
Edit. Since it's a multipartform it's easier to let the framework handle the details. Try something like this:
Server:
[HttpPost]
public async Task<HttpResponseMessage> SaveFile(long docId)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Unauthorized);
try
{
var filedata = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
foreach(var file in filedata.Contents)
{
var fileStream = await file.ReadAsStreamAsync();
}
response = Request.CreateResponse<bool>(HttpStatusCode.OK, true);
}
catch (Exception ex)
{
response = Request.CreateResponse<bool>(HttpStatusCode.InternalServerError, false);
}
return response;
}
At Last I found the solution no need to change the web api service,
issue was from client where I was directly passing the file data, Now the modified
working code is like this:-
using (var formData = new MultipartFormDataContent())
{
var bytes = File.ReadAllBytes(file);
formData.Add(new StreamContent(new MemoryStream(bytes)), "file", file);
HttpResponseMessage responseFile = client.PostAsync("ReportInfo/SaveFile?docId=" + docId, formData).Result;
}

Categories

Resources