Xamarin.iOS 中的文件系統存取
您可以使用 Xamarin.iOS 和 System.IO
.NET 基類庫 (BCL) 中的類別來存取 iOS 檔案系統。 File
類別可讓您建立、刪除和讀取檔案,而 Directory
類別可讓您建立、刪除或列舉目錄內容。 您也可以使用 Stream
子類別,以提供對檔案作業的更大程度的控制(例如壓縮或檔案內的位置搜尋)。
iOS 會對應用程式可以對檔案系統執行哪些動作施加一些限制,以保護應用程式數據的安全性,以及保護使用者免受惡性應用程式的影響。 這些限制是應用程式沙盒的一部分 – 一組規則,可限制應用程式存取檔案、喜好設定、網路資源、硬體等。應用程式僅限於在其主目錄內讀取和寫入檔案(已安裝的位置):它無法存取另一個應用程式的檔案。
iOS 也有一些文件系統特定的功能:某些目錄需要對備份和升級進行特殊處理,而應用程式也可以彼此共用檔案和 檔案 應用程式(自 iOS 11 起),以及透過 iTunes。
本文討論 iOS 檔案系統的功能和限制,並包含範例應用程式,示範如何使用 Xamarin.iOS 來執行一些簡單的檔案系統作業:
一般檔案存取
Xamarin.iOS 可讓您針對 iOS 上的檔案系統作業使用 .NET System.IO
類別。
下列代碼段說明一些常見的檔案作業。 在本文的範例應用程式中,您會在 SampleCode.cs 檔案中找到下列所有內容。
使用目錄
此程式代碼會列舉目前目錄中的子目錄(由 “./” 參數指定),這是應用程式可執行檔的位置。 您的輸出將會是應用程式部署的所有檔案和資料夾清單(在偵錯時顯示在主控台視窗中)。
var directories = Directory.EnumerateDirectories("./");
foreach (var directory in directories) {
Console.WriteLine(directory);
}
讀取檔案
若要讀取文字檔,您只需要一行程序代碼。 本範例會在 [應用程式輸出] 視窗中顯示文字檔案的內容。
var text = File.ReadAllText("TestData/ReadMe.txt");
Console.WriteLine(text);
XML 序列化
雖然使用完整的 System.Xml
命名空間已超出本文的範圍,但您可以使用類似此代碼段的 StreamReader,輕鬆地從文件系統還原串行化 XML 檔:
using (TextReader reader = new StreamReader("./TestData/test.xml")) {
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
var xml = (MyObject)serializer.Deserialize(reader);
}
如需詳細資訊,請參閱 System.Xml 和串行化的檔。 請參閱連結器上的 Xamarin.iOS 檔案 – 您通常需要將 屬性新增[Preserve]
至您想要串行化的類別。
建立檔案和目錄
此範例示範如何使用 Environment
類別來存取可建立檔案和目錄的 Documents 資料夾。
var documents =
Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file");
建立目錄是類似的程式:
var documents =
Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var directoryname = Path.Combine (documents, "NewDirectory");
Directory.CreateDirectory(directoryname);
如需詳細資訊, 請參閱 System.IO API 參考。
串行化 JSON
Json.NET 是一種高效能 JSON 架構,可與 Xamarin.iOS 搭配使用,而且可在 NuGet 上使用。 使用 Visual Studio for Mac 中的新增 NuGet ,將 NuGet 套件新增至您的應用程式專案:
接下來,新增 類別作為串行化/還原串行化的數據模型(在此案例 Account.cs
中為 ):
using System;
using System.Collections.Generic;
using Foundation; // for Preserve attribute, which helps serialization with Linking enabled
namespace FileSystem
{
[Preserve]
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public List<string> Roles { get; set; }
public Account() {
}
}
}
最後,建立 類別的 Account
實例、將它串行化為 json 數據,並將它寫入檔案:
// Create a new record
var account = new Account(){
Email = "monkey@xamarin.com",
Active = true,
CreatedDate = new DateTime(2015, 5, 27, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string> {"User", "Admin"}
};
// Serialize object
var json = JsonConvert.SerializeObject(account, Newtonsoft.Json.Formatting.Indented);
// Save to file
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "account.json");
File.WriteAllText(filename, json);
如需在 .NET 應用程式中使用 json 數據的詳細資訊,請參閱 Json.NET 的檔。
特殊考量
儘管 Xamarin.iOS 和 .NET 檔案作業之間有相似之處,但 iOS 和 Xamarin.iOS 在某些重要方面與 .NET 不同。
讓項目檔在運行時間可供存取
根據預設,如果您將檔案新增至專案,它就不會包含在最終元件中,因此無法供您的應用程式使用。 若要在元件中包含檔案,您必須使用稱為 Content 的特殊建置動作來標記它。
若要標示要包含的檔案,請以滑鼠右鍵按兩下檔案,然後選擇 Visual Studio for Mac 中的 [建置動作 > 內容 ]。 您也可以變更檔案的 [屬性] 工作表中的 [建置動作]。
區分大小寫
請務必瞭解 iOS 文件系統會 區分大小寫。 區分大小寫表示您的檔案和目錄名稱必須完全符合 – README.txt 和 readme.txt 會被視為不同的檔名。
對於更熟悉 Windows 文件系統的 .NET 開發人員來說,這可能會造成混淆,這不區分大小寫 – 檔案、檔案和檔案全都會參考相同的目錄。
警告
iOS 模擬器不區分大小寫。 如果您的檔名大小寫在檔案本身和程式代碼中的參考不同,您的程式代碼仍可能在模擬器中運作,但會在實際裝置上失敗。 這是在 iOS 開發期間早期且經常在實際裝置上部署和測試很重要的原因之一。
路徑分隔符
iOS 使用正斜線 '/'做為路徑分隔符(這與 Windows 不同,它使用反斜杠 '\')。
由於這種混淆差異,所以最好使用 System.IO.Path.Combine
方法,此方法會針對目前的平台進行調整,而不是將特定路徑分隔符硬式編碼。 這是一個簡單的步驟,可讓您的程式代碼更容易移植到其他平臺。
應用程式沙盒
基於安全性考慮,您的應用程式對檔案系統的存取權有限,例如網路和硬體功能。 這項限制稱為 應用程式沙盒。 就檔案系統而言,您的應用程式僅限於在其主目錄中建立和刪除檔案和目錄。
主目錄是檔案系統中儲存應用程式及其所有數據的唯一位置。 您無法為您的應用程式選擇 (或變更) 主目錄的位置;不過,iOS 和 Xamarin.iOS 提供屬性和方法來管理內部的檔案和目錄。
應用程式套件組合
應用程式 套件組合 是包含您應用程式的資料夾。 它會將.app後綴新增至目錄名稱,來區別於其他資料夾。 您的應用程式套件組合包含您的可執行檔,以及專案所需的所有內容(檔案、影像等)。
當您在 Mac OS 中瀏覽至應用程式套件組合時,它會顯示與其他目錄中不同的圖示(且 隱藏.app 後綴):不過,它只是操作系統以不同方式顯示的一般目錄。
若要檢視範例程式代碼的應用程式組合,請以滑鼠右鍵按兩下 Visual Studio for Mac 中的項目,然後選取 [尋找工具中的顯色]。 然後流覽至 您應該找到應用程式圖示的 bin/ 目錄(如下所示的螢幕快照)。
以滑鼠右鍵按下此圖示,然後選擇 [ 顯示套件內容 ] 以瀏覽應用程式套件組合目錄的內容。 內容會像一般目錄的內容一樣出現,如下所示:
應用程式套件組合是在測試期間安裝在模擬器或裝置上,最後是提交至 Apple 以納入 App Store 的內容。
應用程式目錄
當您的應用程式安裝在裝置上時,操作系統會為您的應用程式建立主目錄,並在可供使用的應用程式根目錄中建立一些目錄。 由於 iOS 8,使用者可存取的目錄不在應用程式根目錄內,因此您無法從使用者目錄衍生應用程式套件組合的路徑,反之亦然。
這些目錄、如何判斷其路徑,以及其用途如下:
Directory | 描述 |
---|---|
[ApplicationName].app/ | 在 iOS 7 和更早版本中,這是 ApplicationBundle 應用程式可執行檔儲存所在的目錄。 您在應用程式中建立的目錄結構存在於此目錄中(例如,影像和您在 Visual Studio for Mac 專案中標示為 [資源] 的其他文件類型)。如果您需要存取應用程式套件組合內的內容檔案,可透過 NSBundle.MainBundle.BundlePath 屬性取得此目錄的路徑。 |
檔案/ | 使用此目錄來儲存使用者檔案和應用程式數據檔。 此目錄的內容可透過iTunes檔案共用提供給使用者使用(雖然預設為停用此內容)。 UIFileSharingEnabled 將布爾索引鍵新增至 Info.plist 檔案,以允許使用者存取這些檔案。即使應用程式未立即啟用檔案共用,您也應該避免將此目錄中的使用者隱藏的檔案(例如資料庫檔案,除非您打算共享它們)。 只要敏感性檔案保持隱藏狀態,如果未來版本啟用檔案共享,這些檔案將不會公開(且可能由 iTunes 移動、修改或刪除)。 您可以使用 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments) 方法來取得應用程式 [檔案] 目錄的路徑。iTunes 會備份此目錄的內容。 |
圖書館/ | Library 目錄是儲存使用者未直接建立的檔案的好位置,例如資料庫或其他應用程式產生的檔案。 此目錄的內容永遠不會透過iTunes向用戶公開。 您可以在 Library 中建立自己的子目錄;不過,這裡已經有一些系統建立的目錄,您應該注意,包括喜好設定和快取。 iTunes 會備份此目錄的內容(快取子目錄除外)。 您將備份您在 Library 中建立的自定義目錄。 |
連結庫/喜好設定/ | 應用程式特定的喜好設定檔案會儲存在此目錄中。 請勿直接建立這些檔案。 請改用 類別 NSUserDefaults 。iTunes 會備份此目錄的內容。 |
連結庫/快取/ | Caches 目錄是儲存數據檔的好位置,可協助您的應用程式執行,但可以輕易地重新建立。 應用程式應該視需要建立和刪除這些檔案,並在必要時重新建立這些檔案。 iOS 5 也可能刪除這些檔案(在低記憶體情況下),不過,在應用程式執行時不會這麼做。 iTunes 不會備份此目錄的內容,這表示如果使用者還原裝置,而且安裝應用程式的更新版本之後,這些目錄的內容可能不會存在。 例如,如果您的應用程式無法連線到網路,您可以使用 Caches 目錄來儲存資料或檔案,以提供良好的離線體驗。 應用程式可以在等候網路回應時快速儲存及擷取此數據,但不需要備份,而且在還原或版本更新之後可以輕鬆地復原或重新建立。 |
tmp/ | 應用程式可以儲存此目錄中只需要一小段時間的暫存盤。 為了節省空間,當不再需要檔案時,應該刪除檔案。 當應用程式未執行時,作業系統也可能從這個目錄刪除檔案。 iTunes 不會備份此目錄的內容。 例如,tmp 目錄可能用來儲存下載給用戶顯示的暫存盤(例如 Twitter 虛擬人偶或電子郵件附件),但一旦檢視之後,可能會刪除該檔案(如果未來需要的話,請再次下載)。 |
這個螢幕快照顯示 [尋找工具] 視窗中的目錄結構:
以程式設計方式存取其他目錄
先前的目錄和檔案範例會存取 Documents
目錄。 若要寫入另一個目錄,您必須使用 「..」 語法來建構路徑,如下所示:
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var filename = Path.Combine (library, "WriteToLibrary.txt");
File.WriteAllText(filename, "Write this text into a file in Library");
建立目錄很類似:
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var directoryname = Path.Combine (library, "NewLibraryDirectory");
Directory.CreateDirectory(directoryname);
Caches
目錄 tmp
路徑可以像這樣建構:
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var cache = Path.Combine (documents, "..", "Library", "Caches");
var tmp = Path.Combine (documents, "..", "tmp");
與檔案應用程式共用
iOS 11 引進了 檔案 應用程式 - iOS 的檔案瀏覽器,可讓使用者在 iCloud 中查看並與其檔案互動,並儲存由支援它的任何應用程式儲存。 若要允許使用者直接存取應用程式中的檔案,請在 Info.plist 檔案LSSupportsOpeningDocumentsInPlace
中建立新的布爾索引鍵,並將其設定為 true
,如下所示:
應用程式 的檔案 目錄現在可供在 [檔案 ] 應用程式中流覽。 在 [ 檔案 ] 應用程式中,流覽至 [我的 iPhone ],且每個具有共用檔案的應用程式都會顯示出來。 下列螢幕快照顯示範例應用程式的外觀:
透過 iTunes 與使用者共用檔案
使用者可以編輯Info.plist
和建立支援 [來源] 檢視中 iTunes 共用 (UIFileSharingEnabled
) 專案的應用程式,以存取您應用程式 [檔案] 目錄中的檔案,如下所示:
當裝置已連線,且使用者選擇索引標籤時,可以在iTunes中存取這些 Apps
檔案。例如,下列螢幕快照顯示透過iTunes共用所選應用程式中的檔案:
使用者只能透過 iTunes 存取此目錄中的最上層專案。 他們看不到任何子目錄的內容(雖然可以將它們複製到計算機或刪除它們)。 例如,使用 GoodReader、PDF 和 EPUB 檔案可以與應用程式共用,讓使用者可以在其 iOS 裝置上讀取它們。
如果文件資料夾的內容不小心,修改其內容的使用者可能會造成問題。 您的應用程式應該將此納入考慮,並且能夠復原 Documents 資料夾的破壞性更新。
本文的範例程式代碼會在 Documents 資料夾中建立檔案和資料夾(SampleCode.cs),並在 Info.plist 檔案中啟用檔案共用。 此螢幕快照顯示這些如何在 iTunes 中顯示:
如需如何為應用程式設定圖示,以及您建立的任何自定義檔類型的相關信息,請參閱使用影像一文。
UIFileSharingEnabled
如果機碼為 false 或不存在,則檔案共用預設為已停用,且使用者將無法與您的 Documents 目錄互動。
備份和還原
當 iTunes 備份裝置時,應用程式主目錄中建立的所有目錄將會儲存,但下列目錄除外:
- [ApplicationName].app – 請勿寫入此目錄,因為它已簽署,因此在安裝之後必須維持不變。 它可能包含您從程式代碼存取的資源,但不需要備份,因為它們會透過重新下載應用程式來還原。
- 連結庫/快 取 – 快取目錄適用於不需要備份的工作檔案。
- tmp – 此目錄用於不再需要時建立和刪除的暫存盤,或 iOS 在需要空間時刪除的檔案。
備份大量數據可能需要很長的時間。 如果您決定需要備份任何特定的檔案或數據,您的應用程式應該使用 [檔案和文檔庫] 資料夾。 針對可從網路輕鬆擷取的暫時性數據或檔案,請使用 Caches 或 tmp 目錄。
注意
當裝置在磁碟空間上執行嚴重不足時,iOS 會「清除」文件系統。 此程式將會從目前未執行之應用程式的 Library/Caches 和 tmp 資料夾中移除所有檔案。
符合 iOS 5 iCloud 備份限制
注意
雖然這項原則是首次在 iOS 5 中推出(這似乎是很久以前),但指導方針仍然與今天的應用程式有關。
Apple 引進 了 iOS 5 的 iCloud 備份 功能。 啟用 iCloud 備份時,應用程式主目錄中的所有檔案(不包括通常未備份的目錄,例如,應用程式套件組合和 Caches
tmp
)都會備份到 iCloud 伺服器。 此功能可為使用者提供完整的備份,以防其裝置遺失、遭竊或損毀。
因為 iCloud 只會為每個使用者提供 5 Gb 的可用空間,而且為了避免不必要的使用頻寬,Apple 預期應用程式只會備份必要的用戶產生的數據。 若要遵守 iOS 資料儲存指導方針,您應該遵循下列專案來限制備份的數據量:
- 只會將用戶產生的數據或無法重新建立的數據儲存在 Documents 目錄中(已備份)。
- 儲存任何可以輕鬆地在 或
tmp
中Library/Caches
重新建立或重新下載的其他數據(但未備份,而且可以「清除」)。 - 如果您有適用於 或
tmp
資料夾但Library/Caches
不想「清除」的檔案,請將檔案儲存到別處(例如Library/YourData
),並套用 「不要備份」屬性,以防止檔案使用 iCloud 備份頻寬和儲存空間。 此數據仍會用盡裝置上的空間,因此您應該小心管理它,並盡可能刪除它。
'not back up' 屬性是使用 NSFileManager
類別來設定。 請確定您的類別是 using Foundation
,並呼叫 SetSkipBackupAttribute
如下:
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "LocalOnly.txt");
File.WriteAllText(filename, "This file will never get backed-up. It would need to be re-created after a restore or re-install");
NSFileManager.SetSkipBackupAttribute (filename, true); // backup will be skipped for this file
true
何時SetSkipBackupAttribute
不會備份檔案,而不論檔案儲存在目錄中的目錄為何(甚至是Documents
目錄)。 您可以使用 方法來查詢屬性 GetSkipBackupAttribute
,而且您可以使用 來呼叫 SetSkipBackupAttribute
方法來 false
重設屬性,如下所示:
NSFileManager.SetSkipBackupAttribute (filename, false); // file will be backed-up
在 iOS 應用程式和應用程式延伸模組之間共享數據
由於應用程式延伸模組會以主應用程式的一部分執行(與其包含的應用程式相反),因此不會自動包含數據共用,因此需要額外的工作。 應用程式群組是iOS用來允許不同應用程式共用數據的機制。 如果應用程式已正確設定正確的權利和布建,他們可以存取其一般 iOS 沙箱以外的共享目錄。
設定應用程式群組
共用位置是使用應用程式群組來設定,其設定於iOS 開發人員中心上的 [憑證]、[標識符和配置檔] 區段中。 每個專案的 Entitlements.plist 中也必須參考此值。
如需建立和設定應用程式群組的相關信息,請參閱 應用程式群組功能 指南。
檔案
iOS 應用程式和延伸模組也可以使用一般檔案路徑來共用檔案(因為它們已正確設定正確的權利和布建):
var FileManager = new NSFileManager ();
var appGroupContainer =FileManager.GetContainerUrl ("group.com.xamarin.WatchSettings");
var appGroupContainerPath = appGroupContainer.Path
Console.WriteLine ("Group Path: " + appGroupContainerPath);
// use the path to create and update files
...
重要
如果傳回的群組路徑為 null
,請檢查權利和布建配置檔的組態,並確定其正確無誤。
應用程式版本更新
下載新版本的應用程式時,iOS 會建立新的主目錄,並將新的應用程式套件組合儲存在其中。接著,iOS 會將下列資料夾從舊版的應用程式組合移至新的主目錄:
- 文件
- 程式庫
其他目錄也可以跨目錄複製並置於新的主目錄之下,但不保證會複製它們,因此您的應用程式不應該依賴此系統行為。
摘要
本文說明使用 Xamarin.iOS 的檔案系統作業類似於任何其他 .NET 應用程式。 它也引進了應用程式沙盒,並檢查了它所造成的安全性影響。 接下來,它探索了應用程式套件組合的概念。 最後,它會列舉應用程式可用的特製化目錄,並在應用程式升級和備份期間說明其角色。