Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

31 Dec 2019

UWP文档笔记:文件读写

Files, folders, and libraries阅读笔记。

Enumerate and query files and folders

WinRT可以让APP访问目录,文档库,设备,以及网络上的文件。你可以编写查询来搜寻想要的文件。

WinRT的文件访问有一些需要注意的方面:API一般是异步进行的;文件需要一定的访问权限File access permissions

文中所列举的例子是使用StorageFolder.GetFilesAsync和StorageFolder.GetFoldersAsync来列举KnownFolders.PicturesLibrary下的文件和目录。

或者,也可以使用StorageFolder.GetItemsAsync来同时获取文件和目录。

你可以通过查询的方式,来获取部分感兴趣的文件。文中列举了另一个例子,使用搭配CommonFolderQuery.GroupByMonth使用StorageFolder.CreateFolderQuery,来获取一个StorageFolderQueryResult 对象,里面包含若干个StorageFolder,表征虚拟的目录,每个目录存储有按月分组的文件。

Enumerate and query files and folders

通过FileIO.WriteTextAsync 可以向一个StorageFile写入字符串:

await Windows.Storage.FileIO.WriteTextAsync(sampleFile, "Swift as a shadow");

如果要直接写入字节,需要使用CryptographicBuffer.ConvertStringToBinary 先将字符编码,然后再调用FileIO.WriteBufferAsync写入:

var buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
    "What fools these mortals be", Windows.Security.Cryptography.BinaryStringEncoding.Utf8);

await Windows.Storage.FileIO.WriteBufferAsync(sampleFile, buffer);

如果要以流式(stream)方式写入,那么需要以下几步:

  • 打开一个流: var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
  • 调用IRandomAccessStream.GetOutputStreamAt获取输出流:stream.GetOutputStreamAt(0)
  • 创建一个DataWriter:var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream)
  • 然后调用dataWriter的方法写入:dataWriter.WriteString("DataWriter has methods to write to various types, such as DataTimeOffset.");
  • 同步dataWriter中的数据到流:await dataWriter.StoreAsync();
  • 同步流中的数据到硬盘:await outputStream.FlushAsync();

读取文件内容也有几种方式。一是通过FileIO.ReadTextAsync 直接从StorageFile读取字符串:string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);

以缓冲方式读:

var buffer = await Windows.Storage.FileIO.ReadBufferAsync(sampleFile);
using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
{
    string text = dataReader.ReadString(buffer.Length);
}

以流的方式读:

var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.Read);

using (var inputStream = stream.GetInputStreamAt(0))
{
using (var dataReader = new Windows.Storage.Streams.DataReader(inputStream))
{
    uint numBytesLoaded = await dataReader.LoadAsync((uint)size);
    string text = dataReader.ReadString(numBytesLoaded);
}
}

Best practices for writing to files

这篇介绍Windows.Storage.FileIO和PathIO的下面几个方法:

  • WriteBufferAsync
  • WriteBytesAsync
  • WriteLinesAsync
  • WriteTextAsync

首先,StorageFile并不是传统意义上的Win32文件句柄,只是一个有若干文件操作方法的文件表示,本身不具有太多状态。文件写入方式有前面介绍过的直接写入,缓冲写入或者流式写入。前面两种方式可以看成第三种方式的包装。

实际写入过程非常复杂,需要使用到OpenTransactedWriteAsync来打开一个StorageStreamTransaction,并使用临时文件作为事务的缓存。中间过程会出现各种错误,列举如下:

  • ERROR_ACCESS_DENIED (0X80070005) 文件可能已被删除
  • ERROR_SHARING_VIOLATION (0x80070020) 文件已经被以exclusive write的方式打开。
  • ERROR_UNABLE_TO_REMOVE_REPLACED (0x80070497) 临时文件无法替换目标文件
  • ERROR_DISK_FULL (0x80070070) 顾名思义
  • ERROR_OUTOFMEMORY (0x8007000E) 顾名思义
  • E_FAIL (0x80004005) 其他错误

如果文件正在被写入,那么以FileAccessMode.Read操作打开的读操作会失败,返回ERROR_OPLOCK_HANDLE_CLOSED (0x80070323)。如果遇到此错误,然后再尝试打开文件读取,会导致文件写入出错。(可能文件写入的时候会让读取无效)

如果有多个Write操作指向同一个文件,不能保证那个Write操作的内容会写入。应用需要自己协调Write操作。

出错情况下有可能会出现~Tmp文件残留,可以再App Activation的时候清除这些文件。

当应用切入后台的时候,会有5秒钟清除所有资源。其他参考:App lifecycle 以及 the-lifecycle-of-a-uwp-app BasicSuspension AsyncReaderWriterLock

(未完待续)

comments powered by Disqus