简体   繁体   中英

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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM