Ubicando nuestros ficheros en el dispositivo

En un post anterior vimos la importancia de los datos en nuestras aplicaciones, y todo lo que ello conlleva. Hoy en cambio vamos a hablar del almacenamiento y el acceso a estos datos, o a ficheros disponibles en el dispositivo donde ejecutemos nuestra aplicación.

Al igual que a la hora de elegir el tipo de fichero para almacenar nuestros datos, el lugar donde almacenar o leer estos datos también es importante y variará dependiendo del tipo de archivo que sea y su uso.

Installation Folder

Se trata del directorio creado cuando se instala una aplicación. En él se almacenan todos los ficheros que se adjuntaron a la solución antes de crear el paquete de instalación, por lo que es un directorio solo de lectura.

Su uso principal es para ficheros estáticos o para obtener los datos por defecto de nuestra aplicación, tales como una imagen predeterminada cuando no se ha insertado nada en la aplicación.

Para acceder a estos elementos, debemos indicar su ruta precedida de “ms-appx:/// ”, lo cual sirve para indicar que se trata de un fichero de la carpeta de instalación.

  1: <Image Source="ms-appx:///images/image.png"
  2:        HorizontalAlignment="Center"
  3:        VerticalAlignment="Center"/>
  4:  
  5: var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///texts/text.txt"));

 

Por otro lado, si queremos acceder a estos archivos mediante código también disponemos de la API Windows.ApplicationModel.Package.Current.InstalledLocation, mediante la cual podemos acceder a la ubicación de estos ficheros como a los demás recursos de ficheros.Para ello obtenemos la carpeta empleando esta API, y procederemos a leer los ficheros almacenados, pero sin poder realizarles ninguna modificación.

  1: var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
  2: var files = await folder.GetFilesAsync();

 

App Data folder

Este es el espacio de almacenamiento propio que la aplicación tiene reservado y al cual ninguna otra puede acceder. Está dividido en tres directorios, que son: Roaming, Local y Temp.

Roaming

En esta carpeta se almacenan principalmente los ficheros de configuración que se van a compartir con otras instalaciones de la aplicación, tanto de Windows como de Windows Phone, que estén vinculadas a la misma cuenta de Microsoft Account.

A pesar de que aquí se pueden almacenar más ficheros, solo se sincronizarán hasta llegar al límite establecido, quedando el resto de ficheros almacenados pero no sincronizados.

  1: var roamingData = Windows.Storage.ApplicationData.Current.RoamingFolder;
  2: StorageFile file = await roamingData.CreateFileAsync("file.txt", CreationCollisionOption.OpenIfExists);
  3: var roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
  4: roamingSettings.Values["exampleSettings"] = "Amplio";

Local

Se trata del lugar donde podemos almacenar los datos locales de nuestra aplicación, y que solo estarán disponibles en ese dispositivo. El tamaño máximo de ficheros almacenados aquí es el mismo que la capacidad del dispositivo en cuestión. Estos ficheros pueden ser tanto ficheros de texto en los que volcar datos o ficheros de configuración.

Por otro lado, si actualizamos la aplicación, estos datos se mantendrán.

  1: var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
  2: Object value = localSettings.Values["exampleLocalSettings"];
  3:  
  4: StorageFolder folder = ApplicationData.Current.LocalFolder;
  5: StorageFile file = await folder.CreateFileAsync("file.txt", CreationCollisionOption.OpenIfExists);

 

Temp

En este directorio almacenamos cierta información que consideremos temporal, ya que no tenemos la certeza de que esté ahí la próxima vez que abramos la aplicación. Esto se debe a que estos ficheros pueden ser eliminados en caso de que la capacidad de almacenamiento del dispositivo sea reducida.

  1: StorageFolder folder = ApplicationData.Current.TemporaryFolder;
  2: StorageFile file = await folder.CreateFileAsync("file.txt", CreationCollisionOption.OpenIfExists);

 

 

Por otro lado, disponemos del método ClearAsync, aplicable en las tres carpetas antes mencionadas, que nos sirve para eliminar el contenido completo del directorio indicado, lo cual resulta útil a la hora de devolver la aplicación a su estado inicial.

  1: Windows.Storage.ApplicationData.Current.ClearAsync(Windows.Storage.ApplicationDataLocality.Roaming);

SD Card

Una vez tratado el almacenamiento interno de la aplicación, toca acceder a otras ubicaciones en las cuales poder almacenar datos y leerlos. Muchos dispositivos móviles actuales traen una ranura para tarjeta micro SD, ya que desde Windows Phone 8.1 se puede leer y escribir en las tarjetas externas.

Al considerarse un recurso compartido, si queremos que nuestra aplicación tenga acceso a la tarjeta SD, necesitamos declarar este requisito en el manifiesto de la aplicación. En dicho manifiesto debemos indicar cada tipo de archivo que queremos que sea accesible.

Para obtener los ficheros de la tarjeta SD, lo primero que debemos hacer es comprobar que existe una tarjeta en el dispositivo móvil. Para ello obtenemos el listado de elementos extraíbles de almacenamiento, y si hay algún elemento en esa lista tenemos la carpeta destino.

  1: var devices = Windows.Storage.KnownFolders.RemovableDevices;
  2: var sdCards = await devices.GetFoldersAsync();
  3: if (sdCards.Count == 0) return;
  4: StorageFolder firstCard = sdCards[0];

 

Una vez tenemos el directorio de la tarjeta, ya podemos crear o eliminar carpetas o ficheros de igual manera que lo hacemos en local, pero solo podremos trabajar con los tipos de archivo declarados en el manifiesto.

KnownFolders

El acceso a la API de KnownFolders facilita mucho la vida de los desarrolladores a la hora de buscar archivos dentro del dispositivo. En vez de tener que buscar en todas las ubicaciones posibles, el sistema nos ofrece un listado con todos los archivos de ese tipo concreto, ya sean imágenes, música o vídeos.

Además, los ficheros que almacenemos en KnownFolders estarán disponibles para todas las demás aplicaciones.

Para acceder a los directorios de música, imágenes o vídeos deberemos añadir este requisito en el manifiesto de la aplicación. Tras eso, ya podemos acceder a los elementos que encuentre.

  1: var pictures = await Windows.Storage.KnownFolders.PicturesLibrary.GetFilesAsync();

OneDrive

Uno de las principales características a buscar es el estar siempre conectado, y que nuestros datos se almacenen en algún lugar donde, en caso de que nuestro dispositivo sufra algún percance, nuestra información tenga un respaldo al que acceder.

Ya hemos visto que mediante la carpeta Roaming podemos compartir la configuración, y en menor medida, cantidades pequeñas de datos. Pero en el momento que queremos empezar a almacenar una cantidad mayor de datos, ficheros de tamaño más elevado como imágenes, o elementos similares, nos vemos en la obligación de emplear otras maneras de almacenamiento.

Gracias a la API de Onedrive, podemos almacenar nuestros ficheros en el Onedrive personal de cada usuario, gracias a lo cual tendremos de manera accesible los datos.

Por supuesto, siempre deberemos comprobar mediante código que los elementos han sido encontrados, ya que al ser su Onedrive personal, el usuario los puede mover de sitio sin problemas.

En primer lugar, debemos iniciar sesión con la cuenta de Onedrive del usuario, para así tener acceso a este almacenamiento. Para ello debemos crear una serie de miembros para la conexión, para posteriormente llevar a cabo el proceso de autenticación.

  1: private LiveConnectClient _liveClient;
  2: private LiveAuthClient _liveAuth;
  3: private LiveLoginResult _liveResult;
  4: private LiveConnectSession _liveSession;
  5: private string[] _requiredScopes;
  6:  
  7: public async Task<bool> SignIn(string clientId)
  8: {
  9:     try
  10:     {
  11:         if (_liveSession == null)
  12:         {
  13:             if (_requiredScopes == null)
  14:             {
  15:                 //setting scopes by default
  16:                 _requiredScopes = DefaultScopes;
  17:             }
  18:             _liveAuth = new LiveAuthClient(clientId);
  19:             _liveResult = await _liveAuth.InitializeAsync(_requiredScopes);
  20:             if (_liveResult.Status != LiveConnectSessionStatus.Connected)
  21:                 _liveResult = await _liveAuth.LoginAsync(_requiredScopes);
  22:             _liveSession = _liveResult.Session;
  23:             _liveClient = new LiveConnectClient(_liveSession);
  24:             return true;
  25:         }
  26:     }
  27:     catch
  28:     {
  29:         return false;
  30:     }
  31:     return false;
  32: }

Así, en caso de haber introducido los datos erróneamente o haber cancelado la operación, devolveremos un false, mientras que en caso de que todo haya ido bien, recibiremos una respuesta afirmativa y tendremos ya los datos necesarios almacenados.

Antes de empezar a subir o bajar ficheros debemos saber cómo crear carpetas y acceder a ellas. Empezamos creando una carpeta dentro del Onedrive del usuario, ya que es una de las operaciones básicas para darle un uso mínimamente ordenado al espacio disponible.

Gracias a este método, crearemos una carpeta en la raíz de Onedrive, y devolveremos el identificador de la carpeta.

  1: public async Task<string> CreateFolder(string folderName)
  2: {
  3:     var folderData = new Dictionary<string, object> { { "name", folderName } };
  4:     LiveOperationResult operationResult = await _liveClient.PostAsync("me/skydrive", folderData);
  5:     dynamic result = operationResult.Result;
  6:     string id = string.Format("{0}", result.id);
  7:     return id;
  8: }

 

Posteriormente, debemos ser capaces de acceder a estas carpetas, para así ver qué ficheros contiene. El proceso consiste en que si el usuario esta autenticado en nuestra aplicación, buscaremos la carpeta indicada, devolviéndola en caso de encontrarla, y creándola en caso contrario.

  1: public async Task<string> GetFolder(string folderName)
  2: {
  3:     var folderId = string.Empty;
  4:     //the session is already established, so let's find our folder by its name
  5:     if (_liveClient != null)
  6:     {
  7:         LiveOperationResult result = await 
  8:         _liveClient.GetAsync("me/skydrive/files/");
  9:         var data = (List<object>)result.Result["data"];
  10:         foreach (IDictionary<string, object> content in data)
  11:         {
  12:             if (content["name"].ToString() == folderName)
  13:             {
  14:                 //The folder has been found!
  15:                 folderId = content["id"].ToString();
  16:             }
  17:         }
  18:         //the folder hasn't been found, so let's create a new one
  19:         if (string.IsNullOrEmpty(folderId))
  20:             folderId = await CreateFolder(folderName);
  21:     }
  22:     return folderId;
  23: }

Una vez ya somos capaces de crear carpetas y acceder a ellas, podemos empezar a subir y bajar ficheros.

La subida en un proceso relativamente sencillo, en el cual indicamos la carpeta de destino, el nombre del fichero y los datos a subir, y nuestro elemento LiveConnectClient se encarga de subir el fichero a Onedrive, indicando si se ha podido subir con éxito o no.

  1: public async Task<OperationStatus> UploadFile(string folderName, string fileName, Stream fileStream)
  2: {
  3:     try
  4:     {
  5:         var folderId = await GetFolder(folderName);
  6:         if (_liveClient != null)
  7:         {
  8:             await _liveClient.UploadAsync(folderId, fileName, fileStream, OverwriteOption.Overwrite);
  9:         }
  10:         return OperationStatus.Completed;
  11:     }
  12:     catch
  13:     {
  14:         return OperationStatus.Failed;
  15:     }
  16: }

Por otro lado, la descarga de un fichero varía ligeramente, ya que debe buscar antes el fichero en el espacio en la nube del usuario, y en caso de encontrarlo empezar el proceso de descarga.

  1: public async Task<string> DownloadFile(string folderName, string fileName)
  2: {
  3:     var fileId = string.Empty;
  4:     //looking for the file
  5:     if (_liveClient != null)
  6:     {
  7:         var folderId = await GetFolder(folderName);
  8:         LiveOperationResult fileResult = await _liveClient.GetAsync(folderId + "/files");
  9:         var fileData = (List<object>)fileResult.Result["data"];
  10:         foreach (IDictionary<string, object> content in fileData)
  11:         {
  12:             if (content["name"].ToString() == fileName)
  13:             {
  14:                 //The file has been found!
  15:                 fileId = content["id"].ToString();
  16:                 break;
  17:             }
  18:         }
  19:         await _liveClient.BackgroundDownloadAsync(fileId + "/content",
  20:         new Uri("/shared/transfers/" + fileName, UriKind.Relative));
  21:     }
  22:     return fileId;
  23: }

Para terminar

Acabamos de ver cómo en unos sencillos pasos podemos acceder a múltiples ubicaciones donde almacenar o recoger datos, desde la carpeta de instalación, donde tener los elementos por defecto, hasta Onedrive, donde tener nuestros datos en internet, lo cual nos abre un gran abanico de opciones acerca de cómo guardar nuestra información, lo cual unido a nuestro artículo referente a formas de almacenar los datos puede ayudarnos a aumentar fácilmente las funcionalidades de nuestra aplicación.

Un saludo,

Josevi Agulló (@josevi_7)