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
Related
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...
}
I'm creating a REST API in which accepts a JSON string with 3 different fileUpload in form data.
Here is my API controller post method
public async Task<IHttpActionResult> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
var obj = new SubmitReportModel();
var enggCommentImg = new List<FileModel>();
var concernAreaImg = new List<FileModel>();
var remarkImge = new List<FileModel>();
string UploadPath = ("~/Uploads/ReportsData");
Functions.CreateIfMissing(System.Web.Hosting.HostingEnvironment.MapPath(UploadPath));
foreach (var item in filesReadToProvider.Contents)
{
if (item.Headers.ContentDisposition.Name.Contains("jsonKey"))
{
var jsson = await item.ReadAsStringAsync();
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<SubmitReportModel>(jsson);
}
else if (item.Headers.ContentDisposition.Name.Contains("enggCommentImg"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
enggCommentImg.Add(objfile);
}
else if (item.Headers.ContentDisposition.Name.Contains("concernAreaImg"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
concernAreaImg.Add(objfile);
}
else if (item.Headers.ContentDisposition.Name.Contains("remarkImge"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
remarkImge.Add(objfile);
}
}
return Ok();
}
private async Task<FileModel> SaveFileAsync(byte[] fileDataByteArray)
{
string uploadPath = ("~/Uploads/ReportsData");
var objfile = new FileModel();
var filename = uploadPath + "/" + ApiHelper.GetTimestamp() + ".jpg";
using (FileStream fs = new FileStream(System.Web.Hosting.HostingEnvironment.MapPath(filename), FileMode.OpenOrCreate, FileAccess.Write))
{
await fs.WriteAsync(fileDataByteArray, 0, fileDataByteArray.Length).ConfigureAwait(true);
}
objfile.Name = filename;
objfile.Type = "jpg";
return objfile;
}
This API works fine I got JSON plus multiple files in each file object as well.
It works very well when I debug using breakpoints and parse each step using f10. But when I try it without breakpoints, it only saves the first file.
Here is how I'm calling it using Postman:
I tried the same with making it synchronous but still, I get the same result - saving only one file.
Here is my synchronous POST API code
public object Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
//var filesReadToProvider =Request.Content.ReadAsMultipartAsync().GetAwaiter().GetResult();
var filesReadToProvider = Task.Run(async () => await Request.Content.ReadAsMultipartAsync()).Result;
var obj = new SubmitReportModel();
var enggCommentImg = new List<FileModel>();
var concernAreaImg = new List<FileModel>();
var remarkImge = new List<FileModel>();
string UploadPath = ("~/Uploads/ReportsData");
Functions.CreateIfMissing(System.Web.Hosting.HostingEnvironment.MapPath(UploadPath));
foreach (var item in filesReadToProvider.Contents)
{
if (item.Headers.ContentDisposition.Name.Contains("jsonKey"))
{
var jsson = Task.Run(async () => await item.ReadAsStringAsync()).Result;
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<SubmitReportModel>(jsson);
}
else if (item.Headers.ContentDisposition.Name.Contains("enggCommentImg"))
{
_ = new FileModel();
var fileDataByteArray = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (fileDataByteArray.Length > 0)
{
FileModel objfile = SaveFiles(fileDataByteArray);
enggCommentImg.Add(objfile);
}
}
else if (item.Headers.ContentDisposition.Name.Contains("concernAreaImg"))
{
_ = new FileModel();
var corsrnArr = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (corsrnArr.Length > 0)
{
FileModel objfile = SaveFiles(corsrnArr);
concernAreaImg.Add(objfile);
}
}
else if (item.Headers.ContentDisposition.Name.Contains("remarkImge"))
{
_ = new FileModel();
var remarkarr = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (remarkarr.Length > 0)
{
FileModel objfile = SaveFiles(remarkarr);
remarkImge.Add(objfile);
}
}
}
return 1;
}
private FileModel SaveFiles(byte[] fileDataByteArray)
{
string uploadPath = ("~/Uploads/ReportsData");
var objfile = new FileModel();
var filename = uploadPath + "/" + ApiHelper.GetTimestamp() + ".jpg";
using (FileStream fs = new FileStream(System.Web.Hosting.HostingEnvironment.MapPath(filename), FileMode.OpenOrCreate, FileAccess.Write))
{
fs.Write(fileDataByteArray, 0, fileDataByteArray.Length);
}
objfile.Name = filename;
objfile.Type = "jpg";
return objfile;
}
I'm unable to figure what exactly wrong step I made in it as there is no error in it.
UPDATE:
As theGeneral advised, I do recheck all steps and created a log so
found that all images were getting upload within a second so all of
them receive the same timestamp as name, and as a result, only one
file was getting overwrite again and again.
Keeping this thread as it is as this API work for files + other data
(JSON string in my case) and may get helpful for others
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
I am working a chat application and when when I upload a image with button click then image doesn't received to another device only sender device showing it.
MyButton click code:
public async void Image_Clicked(object sender, EventArgs e)
{
try
{
file = await CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
CompressionQuality = 50,
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
FileName = file.Path;
FileName = FileName.Substring(FileName.LastIndexOf("/") + 1);
var content = new MultipartFormDataContent();
//IFolder rootFolder = await FileSystem.Current.GetFolderFromPathAsync(FileName);
//var filee = await rootFolder.GetFileAsync(filename2);
//Stream stream = await filee.OpenAsync(FileAccess.Read);
content.Add(new StreamContent(file.GetStream()),
"\"file\"",
$"\"{file.Path}\"");
byteData = Model.Convert.ToByteArray(FileName);
var im = ImageSource.FromStream(this.file.GetStream);
using (var memoryStream = new MemoryStream())
{
activityIndicator.IsRunning = true;
file.GetStream().CopyTo(memoryStream);
file.Dispose();
// return memoryStream.ToArray();
uploadedFilename = await AzureStorage.UploadFileAsync(ContainerType.Image, new MemoryStream(memoryStream.ToArray()));
}
if (!string.IsNullOrWhiteSpace(uploadedFilename))
{
var imageData = await AzureStorage.GetFileAsync(ContainerType.Image, uploadedFilename);
var img = ImageSource.FromStream(() => new MemoryStream(imageData));
TextContainer.Add(new MessageText { imgsource = img });
activityIndicator.IsRunning = false;
}
}
catch( Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
I am Also using ObservableCollection but unable to get image to second device:
public ObservableCollection<MessageText> TextContainer { get; set; } = new ObservableCollection<MessageText>();
Upload fileCode which I have used to upload my file:
public static async Task<string> UploadFileAsync(ContainerType containerType, Stream stream)
{
var container = GetContainer(containerType);
await container.CreateIfNotExistsAsync();
var name = Guid.NewGuid().ToString();
var fileBlob = container.GetBlockBlobReference(name);
await fileBlob.UploadFromStreamAsync(stream);
return name;
}
I have used SignalR and its working fine for me
I'm getting a web response via an API which converts file (in this case, Powerpoint presentations to PDF).
I do get the response in a string, but when saving this string to a file or a stream (which ends up being saved in a file anyways) I always end up with a blank file, its size is always well over 0 bytes though.
Here's the class that calls the API:
public class CloudConvert
{
private string apiKey;
private static object ProcessResponse;
public CloudConvert(string apiKey)
{
this.apiKey = apiKey;
}
public async Task<string> Convert(string inputFormat, string outputFormat, string fileUrl)
{
var processUrl = await CreateProcess(inputFormat, outputFormat);
return await Upload(processUrl, fileUrl, outputFormat);
}
private async Task<string> CreateProcess(string inputFormat, string outputFormat)
{
var request = new
{
apikey = apiKey,
inputformat = inputFormat,
outputformat = outputFormat
};
var json = await PostJson("https://api.cloudconvert.com/process", request);
dynamic obj = JObject.Parse(json);
ProcessResponse = obj;
return "https:" + obj.url;
}
private static async Task<string> Upload(string processUrl,
string fileUrl, string outputFormat)
{
var request = new
{
input = "download",
file = fileUrl,
outputformat = outputFormat,
download = "false",
wait = "true",
save = "false"
};
return await PostJson(processUrl, request);
}
private static async Task<string> PostJson(string url, object data)
{
var parameters = JsonConvert.SerializeObject(data);
using (var wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/json";
try
{
return await wc.UploadStringTaskAsync(url, "POST", parameters);
}
catch (WebException ex)
{
return string.Empty;
}
}
}
}
And how I'm invoking it:
Task<string> task = Task.Run(async () =>
{
return await cloudConvert.Convert("ppt", "pdf", "http://host.com/myfile.ppt");
});
//I have the PDF in the response, as a string.
var pdfContent = task.Result;
//I think it's here that I'm not handling the resulting string
//properly, ending in a blank file. I've tried writing it to a local file as well, same issue.
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream);
writer.Write(pdfContent);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
//This below is something that saves to an Azure storage container...
//filePath = fileStorage.SaveFile($"{fileGeneratedName}.pdf", stream);
//But this fails as well. Blank file
using (FileStream fs = new FileStream(#"c:\tmp\output.pdf", FileMode.OpenOrCreate))
{
stream.CopyTo(fs);
fs.Flush();
}
}
So regardless of how I'm trying to save the returned content, it seems my encoding is wrong. I noticed that the resulting blank file always has the same number of pages than the input Powerpoint presentation.
Any ideas?
Have your convert function return an HttpResponseMessage:
public async Task<HttpResponseMessage> convert()
do your conversion into, say, a memory stream, and then:
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(outStream.GetBuffer(),0,(int)outStream.Length)
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = fname
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;