How to write a stream to a file in Windows Runtime? - c#

There are lots of questions around, but mostly are about writing strings to a file. I'm a bit confused!
What is the proper way to write a stream to a file?
what I did:
Stream stream = await GetStream(source);
var file = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("TempUsersFile", CreationCollisionOption.ReplaceExisting);
var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite);
using (var dataWriter = new Windows.Storage.Streams.DataWriter(fileStream))
{
// How can I write to buffer and write to the file
}

There are many ways of writing a stream to file and they also depend on your needs. The method below performs asynchronous operation with a specified buffer:
public async Task SaveStreamToFile(Stream streamToSave, string fileName, CancellationToken cancelToken)
{
StorageFile file = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (Stream fileStram = await file.OpenStreamForWriteAsync())
{
const int BUFFER_SIZE = 1024;
byte[] buf = new byte[BUFFER_SIZE];
int bytesread = 0;
while ((bytesread = await streamToSave.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
{
await fileStram.WriteAsync(buf, 0, bytesread);
cancelToken.ThrowIfCancellationRequested();
}
}
}
I've implemented also a CancellationToken in case there will be a need to cancel the long running Task.

I would recommend using the StreamReader and StreamWriter classes:
StreamWriter sw = new StreamWriter(outstream);
StreamReader sr = new StreamReader(instream);
while (!sr.EndOfStream)
{
int ch = sr.Read();
sw.Write(ch);
}

Related

InputFile handle multiple files in parallel

I want to be able to upload multiple files via FileInput but got stuck when it comes to parallelism.
I simply want to show the user a progress-bar (depending on overall read bytes) and afterwards a simple list of what has already been processed.
Currently my callback is looking like this:
private async Task HandleInputFileChange(InputFileChangeEventArgs fileChangeEventArgs)
{
_filesProcessed.Clear();
_alreadyRead = 0;
var browserFiles = fileChangeEventArgs.GetMultipleFiles();
_max = browserFiles.Sum(bf => bf.Size);
await Task.WhenAll(browserFiles.Select(browserFile => Task.Run(async () =>
{
var trustedFileName = Path.GetRandomFileName();
var filePath = Path.Combine(HostEnvironment.ContentRootPath, HostEnvironment.EnvironmentName, FolderName, trustedFileName);
await using var fileStream = new FileStream(filePath, FileMode.Create);
await using var readStream = browserFile.OpenReadStream(AllowedFileSize);
int bytesRead;
var readBuffer = new byte[1024 * 10];
while ((bytesRead = await readStream.ReadAsync(readBuffer)) != 0)
{
_alreadyRead += bytesRead;
await fileStream.WriteAsync(readBuffer, 0, bytesRead);
await InvokeAsync(StateHasChanged);
}
_filesProcessed.Add(browserFile);
})));
}
However, with this code I mostly end up in a NullReferenceException at
Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream.ReceiveData(RemoteJSRuntime runtime, Int64 streamId, Int64 chunkId, Byte[] chunk, String error)
Currently I'm not even sure if this is possible to do or not as it may seem to be an issue with how things get synchronized by the framework.

How to receive a large file(>2GB) in ASP .Net WebAPI with out multipart MIME type?

Is there any way to implement a .net Web api to receive a lrage file(>2gb) using bufferlessinput stream without multipart/form-data mime type?
I am trying to do it with below code, but its not reading the stream completely. I am trying to upload 100 MB file, but it writes only 10MB to "c:\sampl.zip" and comes out. what went wrong in below code?
public async Task<HttpResponseMessage> FileReceive1r(string id)
{
var content = new StreamContent(HttpContext.Current.Request.GetBufferlessInputStream(true));
Stream stream = HttpContext.Current.Request.GetBufferlessInputStream(true);
StreamReader rdr = new StreamReader(stream);
while(!rdr.EndOfStream)
{
//FileStream fs = new FileStream(#"c:\sampl.zip", FileMode.OpenOrCreate);
StreamWriter wrtr = new StreamWriter(new FileStream(#"c:\sampl.zip", FileMode.OpenOrCreate));
wrtr.Write(rdr.ReadToEnd());
wrtr.Close();
}
rdr.Close();
return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.Created));
}
You can use following code to download the file in chunk
public HttpResponseMessage Get()
{
string filename = #"c:\sampl.zip";
var response = this.Request.CreateResponse();
response.Content = new PushStreamContent(async (Stream outputStream, HttpContent content, TransportContext context) =>
{
try
{
var buffer = new byte[65536];
using (var video = File.Open(filename, FileMode.Open, FileAccess.Read))
{
var length = (int)video.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
length -= bytesRead;
}
}
}
finally
{
outputStream.Close();
}
});
return response;
}

UWP: async read file into byte[]

I want to read a locally stored file into a byte array. How do I do that? This is my try:
StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(filePath);
var file = await folder.GetFileAsync(filePath);
var buffer = await FileIO.ReadBufferAsync(file);
DataReader dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer);
// doesn't work because ReadBytes wants a byte[] as parameter and also isn't asynchronous
byte[] result = dataReader.ReadBytes(buffer.Length);
I think the other answers make things unnecessarily complicated. There is a convenient extension method IBuffer.ToArray() for this purpose.
Simply do this:
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage;
...
IStorageFile file;
IBuffer buffer = await FileIO.ReadBufferAsync(file);
byte[] bytes = buffer.ToArray();
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.GetFileAsync(FILE_NAME);
byte[] result;
using (Stream stream = await sampleFile.OpenStreamForReadAsync())
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
result = memoryStream.ToArray();
}
}
Three concepts come to my mind - using FileStream and modyfing your method little:
the first reads bytes via a provided buffer, though this method needs seekable stream:
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt");
using (Stream fileStr = await (await picker.PickSingleFileAsync()).OpenStreamForReadAsync())
{
byte[] bytes = new byte[fileStr.Length];
const int BUFFER_SIZE = 1024;
byte[] buffer = new byte[BUFFER_SIZE];
int position = 0;
int bytesread = 0;
while ((bytesread = await fileStr.ReadAsync(buffer, 0, BUFFER_SIZE)) > 0)
for (int i = 0; i < bytesread; i++, position++)
bytes[position] = buffer[i];
}
the second method asynchronously copies filestream to memory then gets it as an array:
using (MemoryStream memStream = new MemoryStream())
using (Stream fileStr = await (await picker.PickSingleFileAsync()).OpenStreamForReadAsync())
{
await fileStr.CopyToAsync(memStream);
byte[] bytes = memStream.ToArray();
}
your method with little mdification - processing via memory stream:
var buffer = await FileIO.ReadBufferAsync(file);
using (MemoryStream mstream = new MemoryStream())
{
await buffer.AsStream().CopyToAsync(mstream);
byte[] result = mstream.ToArray();
}
Or maybe better you can avoid using byte[] and instead use IBuffer or MemoryStream.

How to save data from stream to file in WinRT?

I would like to have a method which takes as an input a System.IO.Stream and use it to write data from it to file. So far I have the following:
public async Task SaveStreamToFileX(Stream stream, string filePath, IProgress<long> progress)
{
var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetDirectoryName(filePath));
var fileName = Path.GetFileName(filePath);
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
var istream = stream.AsInputStream();
var canRead = stream.CanRead; //this returns true
using (var reader = new DataReader(istream))
{
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream outputStream = fileStream.GetOutputStreamAt(0))
{
using (DataWriter writer = new DataWriter(outputStream))
{
IBuffer buffer;
long readBytes = 0;
const int bufferSize = 8192;
while ((buffer = reader.ReadBuffer(bufferSize)).Length > 0) //exception System.Exception with message: Out of range ...
{
writer.WriteBuffer(buffer);
readBytes += bufferSize;
progress.Report(readBytes);
}
}
}
}
}
}
Problem is, that an exception (Out of range) is thrown when I try to read data in while cycle (first read). Stream should have data. I am not sure if so long code is neccesarry, if somebody has better solution it would be great.
Side note:
If I try await reader.LoadAsync(50) it returns 50. I am not sure what LoadAsync should do. Maybe I have to call it before read to prepare data for the read? I will investigate this further ...
Also, the Stream.CanRead returns true.
The problem was not with converting streams as I orginally thought. It was just lack of the knowlidge how the work with files is done in WinRT (the docs from microsoft are really terrible in my opinion).
In the end with help of my collegue with tried several ways and end up with the following:
public async Task SaveStreamToFile(Stream stream, string filePath, IProgress<long> progress )
{
var folder = await StorageFolder.GetFolderFromPathAsync(Path.GetDirectoryName(filePath));
var fileName = Path.GetFileName(filePath);
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
var istream = stream.AsInputStream();
using (var reader = new DataReader(istream))
{
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream outputStream = fileStream.GetOutputStreamAt(0))
{
using (DataWriter writer = new DataWriter(outputStream))
{
long writtenBytes = 0;
const int bufferSize = 8192;
uint loadedBytes = 0;
while ((loadedBytes = (await reader.LoadAsync(bufferSize))) > 0) //!!!
{
IBuffer buffer = reader.ReadBuffer(loadedBytes);
writer.WriteBuffer(buffer);
uint tmpWritten = await writer.StoreAsync(); //!!!
writtenBytes += tmpWritten;
progress.Report(writtenBytes);
}
}
}
}
}
}
I would like to see some simpler implementation, but this works. The problems were that LoadAsync was missing (which seems to be necessary to call) and during write operation the StoreAsync must be called in order to commit the data (flushing was not sufficient).
I hope this help somebody.
I would advise against that kind of code and instead take advantage of Windows Runtime Interop extension methods. That would produce a neater and more readable code, ex :
private async Task CopyToTempFile(Stream stream, string temporaryFileName) {
var file = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(temporaryFileName, CreationCollisionOption.ReplaceExisting);
using (var outputstream = await file.OpenStreamForWriteAsync()) {
await stream.CopyToAsync(outputstream);
}
}

Write (Read) IO.Stream to (from) ApplicationData.Current.LocalFolder

What is the best way to read amnd write and IO.Stream (Zip file downloded from internet in my case) to ApplicationData.Current.LocalFolder
I tried
public static async Task WriteToFile(
this System.IO.Stream input,
string fileName,
StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(
fileName,
CreationCollisionOption.ReplaceExisting);
using (var fs = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (var outStream = fs.GetOutputStreamAt(0))
{
using (var dataWriter = new DataWriter(outStream))
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
dataWriter.WriteBytes(buffer);
}
await dataWriter.StoreAsync();
dataWriter.DetachStream();
}
await outStream.FlushAsync();
}
}
}
for writing and
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(filename);
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var stream = fileStream.AsStreamForRead();
but the file gets corrupted somewhere along the way.I do no think there is a problem with reading so it should be somewhere in writing the file. Is there a better way to write IO.Stream to ApplicationData.Current.LocalFolder that works?
Try this:
static async void DownloadFileAsync(
this HttpClient httpClient,
string requestUri,
string fileName,
StorageFolder folder = null)
{
folder = folder ?? ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(
fileName, CreationCollisionOption.ReplaceExisting);
using (var httpStream = await httpClient.GetStreamAsync(uri))
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await httpStream.CopyToAsync(fileStream.AsStreamForWrite());
}
}
MSDN: Http­Client Class, Http­Client.­Get­Stream­Async Method, Windows­Runtime­Stream­Extensions.­As­Stream­For­Write Method, Stream.­Copy­To­Async Method.

Categories

Resources