При разработке приложений, использующих файлы данных, необходимо учитывать несколько аспектов доступа к файлам, особенно при создании для такой платформы, как универсальная платформа Windows (UWP). Прежде чем мы углубимся в особенности того, как это сделать с помощью приложений UWP (читай: HoloLens), следует помнить три важных момента:
- Универсальные приложения Windows не создаются с использованием одних и тех же Win32 API. Хотя многие из них стали доступны в Windows 10, некоторые из них нельзя использовать в приложениях UWP, а это означает, что вам может потребоваться переключить некоторые стратегии, если вы полагаетесь на старые API.
- Пути к файлам на устройствах могут совпадать, а могут и не совпадать, поэтому важно сделать это правильно, когда вы его программируете (читай: не просто жестко закодируйте ссылку!)
- Вы (имеется в виду ваш код) должны иметь соответствующие разрешения для доступа к файлам. В этом посте я специально сосредоточусь на чтении файловых данных, но это также невероятно важно, если ваше приложение также использует запись данных в файл.
Для контекста: я создаю приложение для визуализации явки избирателей, интерактивное голографическое приложение для Microsoft HoloLens, которое показывает исторические данные о явке избирателей в Соединенных Штатах во время президентских выборов за последние 36 лет. В рамках приложения я решил сделать его автономным, и у меня было несколько текстовых файлов в формате JSON, содержащих всю информацию, которую я буду отображать.
{ "Year": 1980, "ICPSR State Code": 40, "Alphanumeric State Code": 47, "Geography": "Virginia", "VEP Highest Office": "48.7%", "Total Ballots Counted": "", "Voting-Eligible Population (VEP)": "3,830,887" }
Пример данных из штата Вирджиния за 1980 год.
Каждый файл сохраняется по годам — и у меня есть файлы данных по всем выборам, всего 18 файлов, которые разбивают население, имеющее право голоса (VEP), и явку на высший пост. Чтобы упростить первый проход, я решил сначала протестировать только годы президентских выборов, в результате чего получилось 9 файлов.
Первая важная часть возможности доступа к этим файлам из универсального приложения Windows заключалась в том, чтобы убедиться, что я сохраняю их в месте, доступном для устройства, независимо от того, где я запускаю приложение. У меня было два варианта: я мог хранить файлы или данные из них в Azure и создать API, чтобы вызывать их во время выполнения, или я мог хранить их в папке, которая будет включена в мой проект при его создании. Поскольку я работал с конечным набором данных (и при этом относительно небольшим), я выбрал второй вариант и воспользовался возможностью Unity StreamingAssets.
Unity StreamingAssets позволяет разработчикам указать точное расположение файла для своего приложения, которое сохраняется во время сборки — хотя разные платформы обрабатывают это по-разному, для приложения UWP это означает, что мы можем хранить файлы в нашей сборке таким образом, чтобы мы знаем, что они будут сохранены по определенному пути. Настроить StreamingAssets очень просто:
- Создайте папку с именем «StreamingAssets» в вашем каталоге /Assets/ в Unity — это чувствительно к регистру, поэтому убедитесь, что она сохранена именно так, как показано.
- Добавьте в свои файлы данных, к которым вы будете обращаться из своего кода, в папку StreamingAssets.
Для UWP это теперь сделано! Довольно просто, правда? Если вы создадите свое приложение для универсальной платформы Windows и откроете решение в Visual Studio, вы увидите каталог StreamingAssets, сохраненный в файле данных.
Следующее, что нужно рассмотреть, это как получить доступ к файлу из кода C#. В моем приложении я хотел загрузить эту информацию при первом запуске приложения, чтобы настроить наш объект карты и получить данные в пригодном для использования состоянии. Это означает, что нам нужно сделать две вещи: сформировать наш полный путь и прочитать данные в виде потока байтов.
Форматирование пути
Хотя, безусловно, было бы неплохо просто указать относительный путь для наших данных, он меняется от файловой системы к файловой системе и от одного пользователя к другому, поэтому нам нужно сформировать наш путь для нашего приложения. Мы можем сделать это с помощью следующей функции:
string GetFilePath(string fileName) { return Path.Combine(Application.streamingAssetsPath, fileName); }
Вы должны убедиться, что строка имени вашего файла включает формат файла — в моем случае это был .JSON — но мы можем позволить Path.Combine сделать все остальное. Эта функция гарантирует, что независимо от того, где находится наша сборка в файловой системе, она создает правильное расположение для наших файлов данных с учетом имени файла и пути StreamingAssets.
Потоковая передача данных
Признаюсь, эта часть всегда немного сбивает меня с толку всякий раз, когда я сажусь читать поток байтов из файла. Этот проект ничем не отличался — я фактически закончил большую часть форматирования данных и визуализации Unity, прежде чем смог заставить работать потоковую передачу, но оказалось, что я упустил довольно простое решение.
Во введении я упомянул, что не все Win32 API поставляются как часть универсальной платформы Windows — и оказалось, что класс System.IO.FileStream, который я использовал в редакторе Unity, не был одним из них. Поэкспериментировав с StorageFile API, который представляет собой решение UWP для чтения файловых данных, я сделал захватывающее открытие: класс UnityEngine.Windows, встроенный в Unity, содержит средство чтения файлов, не использующее асинхронные методы.
Код потоковой передачи файла Win32:
string fileName = Application.streamingAssetsPath +"\\" + YOURFILENAME; byte[] result; using (FileStream SourceStream = System.IO.File.Open(fileName, FileMode.Open)) { result = new byte[SourceStream.Length]; SourceStream.Read(result, 0, (int)SourceStream.Length); return System.Text.Encoding.ASCII.GetString(result); }
Код потоковой передачи файла UnityEngine.Windows:
string fileData = ""; string fileName = Path.Combine(Application.streamingAssetsPath, YOURFILENAME); byte[] bytes = UnityEngine.Windows.File.ReadAllBytes(fileName); fileData = System.Text.Encoding.ASCII.GetString(bytes); return fileData;
Если вы хотите прочитать информацию из файла в приложении UWP или HoloLens, я настоятельно рекомендую использовать решение UnityEngine.Windows, если это возможно.
Когда решение было, наконец, построено, я был очень рад видеть, что все мои данные о голосовании по состоянию были загружены правильно, независимо от того, запускал ли я их в редакторе, на своем локальном компьютере или прямо на HoloLens. Я назвал каждую из различных функций выше в зависимости от платформы, используя #if WINDOWS_UWP, но я не удивлюсь, если решение UnityEngine.Windows будет работать независимо от архитектуры. Как человек, который все еще работает над изучением тонкостей многопоточности и асинхронного программирования, решение UnityEngine.Windows сэкономило значительное количество времени на решение для вывода Visual Studio.
Взломайте!