I need to process an XML file once it is uploaded. How do I check if the files are completely uploaded before I start the new method to do work on the file?
[HttpPost]
public async Task<IActionResult> UploadFile(ICollection<IFormFile> files)
{
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
foreach (var file in files)
{
if (file.Length > 0)
{
fileName = Path.GetFileName(file.FileName);
using (var fileStream = new FileStream(Path.Combine(uploads, fileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
}
ProcessFile();
return View("NewProject");
}
private void ProcessFile()
{
//Do WORK
}
Right now it starts to Process the file while it's still being uploaded. How can I wait till upload is finished and check that the file is uploaded before Processing the File?
Have a look at this link FileStream Reading. As per your problem, you need to do a length check for the contents of the file and when all the content is read, do the await call.
Related
I am currently uploading a file via the kendo fileuploader to an api controller using ASP.NET core RC-1. I am receiving a periodic error of "object reference not set to instance of object" when attempting to read the stream following opening the stream with IFormFile.OpenReadStream().
My controller is:
[HttpPost]
[Route("api/{domain}/[controller]")]
public async Task<IActionResult> Post([FromRoute]string domain, [FromForm]IFormFile file, [FromForm]WebDocument document)
{
if (ModelState.IsValid)
{
if (file.Length > 0)
{
var userName =
Request.HttpContext.User.Claims
.FirstOrDefault(c => c.Type == ClaimTypesEx.FullName)?
.Value;
var uploadedFileName =
ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
document.Domain = domain;
document.MimeType = file.ContentType;
document.SizeInBytes = file.Length;
document.ChangedBy = userName;
document.FileName = (string.IsNullOrEmpty(document.FileName)) ? uploadedFileName : document.FileName;
try
{
document = await CommandStack.For<WebDocument>()
.AddOrUpdateAsync(document, file.OpenReadStream()).ConfigureAwait(false);
}
catch (Exception e)
{
return new HttpStatusCodeResult(500);
}
return Ok(document);
}
}
return new BadRequestResult();
}
And the error is being thrown when I actually try to read the stream when it is going into blob storage:
public async Task<Uri> CreateOrUpdateBlobAsync(string containerName, string fileName, string mimeType,
Stream fileStream)
{
var container = Client.GetContainerReference(containerName);
var blob = container.GetBlockBlobReference(fileName);
//Error HERE
await blob.UploadFromStreamAsync(fileStream);
blob.Properties.ContentType = mimeType;
await blob.SetPropertiesAsync();
return blob.Uri;
}
What I am having trouble with is this is sporadic and there seems to be no defined pattern of which files are accepted and which ones generate the error. At first I thought it might be a size issue but that is not the case as I have several larger files uploaded successfully and then one small file will throw the error. Images seem to work fine and it is hit or miss on other file types with no rhyme or reason that I can figure out.
I have a file being uploaded using http post request using multipart/form-data to my class that is extending from ApiController.
In a dummy project, I am able to use:
HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase
to get the file inside my controller method where my Request is of type System.Web.HttpRequestWrapper.
But inside another production app where I have constraints of not adding any libraries/dlls, I don't see anything inside System.Web.HttpRequestWrapper.
My simple requirement is to get the posted file and convert it to a byte array to be able to store that into a database.
Any thoughts?
This code sample is from a ASP.NET Web API project I did sometime ago. It allowed uploading of an image file. I removed parts that were not relevant to your question.
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
var provider = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
var firstImage = provider.Contents.FirstOrDefault();
if (firstImage == null || firstImage.Headers.ContentDisposition.FileName == null)
return Request.CreateResponse(HttpStatusCode.BadRequest);
using (var ms = new MemoryStream())
{
await firstImage.CopyToAsync(ms);
var byteArray = ms.ToArray();
}
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
My server has an API to upload files and convert them to PDF. Right now, the file gets uploaded, saved to disk and then converted. See the (trimmed down) code below:
public class ConversionController : ApiController {
public async Task<HttpResponseMessage> PostData() {
var root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
var file = provider.FileData.First();
var originalName = file.Headers.ContentDisposition.FileName;
var fileStream = new FileStream(file.LocalFileName, FileMode.Open, FileAccess.Read);
// convert file stream and return the PDF response ...
}
}
As you can see, I read the file to disk but then immediately get a stream for it so I can feed it to our conversion function (which takes a stream). This seems like a waste to save the file to disk every time. So instead of ReadAsMultipartAsync() which saves to disk, I can use ReadAsStreamAsync() which will give me the stream that I can give directly to the conversion function.
The problem that I'm having with ReadAsMultipartAsync() is that I can't figure out how to get the original file name without having the MultipartFileData instance to work with. I know that the name comes with the request as part of the body, but I can't figure out how to access it. How can I get the name of the uploaded file without writing the uploaded file to disk?
You can use MultipartMemoryStreamProvider, for example:
var provider = new MultipartMemoryStreamProvider();
var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
{
var file = provider.Contents.First();
var fileContents = await file.ReadAsByteArrayAsync();
var filename = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
/// do other stuff
return Request.CreateResponse(HttpStatusCode.OK);
});
In this case the content is read as a byte array, but the same applies to streams.
I'm working on a web api's using ASP.NET MVC's which download the zip file attached in the HttpRequestMessage
as below
var task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
if (this.Request.Content.IsMimeMultipartContent())
{
using (Stream requestStream = task.Result)
{
// Do not know how to copy the above request to file stream or zip (ionic zip) and generate zip file from it
}
}
note:
1)
using (var fileStream = File.Create("name.zip"))
{
requestStream.CopyTo(fileStream);
}
creates invalid zip..
2) Zip contains many files inside it.
Waiting for your comments
Update 1:
var provider = new MultipartFormDataStreamProvider(ScriptPath);
Request.Content.ReadAsMultipartAsync(provider);
foreach (MultipartFileData file in provider.FileData)
{
Trace.Write(file.Headers.ContentDisposition.FileName);
Trace.Write("Server file path: " + ScriptPath);
}
though does not help me
This works for me. Please treat as pseudo as I didn't compile it. Also you may want to rework the blocking .Result call to take advantage of the Async read.
var requestStream = await Request.Content.ReadAsMultipartAsync();
HttpContent fileContent = requestStream.Contents.SingleOrDefault(c => c.Headers.ContentType != null);
byte[] fileBytes = await fileContent.ReadAsByteArrayAsync();
using (FileStream fs = File.OpenWrite(outputPath))
{
fs.Write(fileBytes, 0, (int)fileContent.Headers.ContentLength.Value);
}
I am making a windows phone 8 application. Part of this application requires state to be saved. I am saving it as a string of Json. If I open the application, save some data, exit the application and the load it again, it hangs on either GetFolderAsync or OpenStreamForReadAsync. It does not happen every time, but once it starts hanging, I have to kill the whole emulator and make a new one to start the application again.
I have even tried just making an empty file with no data in it and the problem still persistes.
Below is the code I am using to save and load the data. It does not matter where I call the data load whether it be on application start or on the form load it still breaks.
private async Task SaveLists()
{
//XmlSerializer serializer = new XmlSerializer(typeof(ListHolder));
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
// Create a new folder name DataFolder.
var dataFolder = await local.CreateFolderAsync("DataFolder",
CreationCollisionOption.OpenIfExists);
// Create a new file named DataFile.txt.
var file = await dataFolder.CreateFileAsync("Lists.json",
CreationCollisionOption.ReplaceExisting);
string json = JsonConvert.SerializeObject(Lists, Formatting.Indented);
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(json.ToCharArray());
using (var s = await file.OpenStreamForWriteAsync())
{
s.Write(fileBytes, 0, fileBytes.Length);
}
}
private async Task LoadLists()
{
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
if (local != null)
{
try
{
// Get the DataFolder folder.
var dataFolder = await local.GetFolderAsync("DataFolder");
// Get the file.
var files = dataFolder.GetFilesAsync();
var file = await dataFolder.OpenStreamForReadAsync("Lists.json");
string jsonString = "";
// Read the data.
using (StreamReader streamReader = new StreamReader(file))
{
jsonString = streamReader.ReadToEnd();
}
if (jsonString.Length > 0)
{
Lists = JsonConvert.DeserializeObject<List<ItemList>>(jsonString);
}
else
{
Lists = new List<ItemList>();
}
}
catch (Exception ex)
{
Lists = new List<ItemList>();
}
}
}
You are causing a deadlock by calling Result. I explain this deadlock on my blog and in a recent MSDN article. In summary, await will (by default) attempt to resume execution within a context (the current SynchronizationContext unless it is null, in which case it uses the current TaskScheduler).
In your case, the current SynchronizationContext is the UI context, which is only used by the UI thread. So when you block the UI thread by calling Result, the async method cannot schedule back to the UI thread to complete.