Este post es my básico técnicamente. Decidí escribirlo por qué muchas veces la gente que va comenzando con Xamarin necesita trabajar con archivos locales y normalmente al investigar uno encuentras soluciones donde debe escribir código especifico para Android y iOS; aunque eso funciona bien, hay ocasiones donde podemos simplificar el trabajo con algunos métodos como los siguientes.

Nota: Todo el código esta creado para .NET Standard (en PCL no se tiene acceso a las clases necesarias).

Leer archivos dentro del paquete de la app

Cuando necesitamos leer un archivo que vamos a dejar fijo dentro del paquete podemos realizar lo siguiente.

Primero, agregar el archivo al proyecto, poniéndole como “Build Action” (Acción de compilación) el valor de  “EmbeddedResource”

En el proyecto .NET Standard podemos hacer uso del siguiente método

public static string ReadFileInPackage(string name)
        {
            string fileContent = string.Empty;

            var assembly = typeof(LocalFilesHelper).GetTypeInfo().Assembly;

            var resourceName = assembly.GetManifestResourceNames().Where(r => r.ToLowerInvariant().EndsWith(name.ToLowerInvariant(), StringComparison.Ordinal)).FirstOrDefault();

            if (resourceName != default(string))
            {
                using (Stream fileStream = assembly.GetManifestResourceStream(resourceName))
                {
                    fileStream.Seek(0, SeekOrigin.Begin);
                    using (var fileReader = new StreamReader(fileStream))
                    {
                        fileContent = fileReader.ReadToEnd();
                    }
                }
            }

            return fileContent;
        }

 

Básicamente lo que hace el método es leer todos los recursos del Assembly y luego obtiene el primero que coincida con el nombre que le pasamos cómo parámetro.

Nota: La razón por la que no voy directamente al recurso, es por qué hace algunos años dependiendo de la versión de Visual Studio con la que estuviera trabajando había diferencias en el nombre del archivo cuando se concatenaba con los datos del assembly y eso causaba errores.

Esta versión es la que me ha funcionado sin problemas y cómo generalmente uso pocos archivos no he tenido problemas por esa penalización en rendimiento.

Leer un archivo comprimido en zip

En algún proyecto ocupe archivos .zip dentro del paquete de la app, debido a que eran xml y al comprimirlos se ahorraba mucho en el tamaño del paquete de la app.

Para poder descomprimirlos se puede usar método como este.

public static string ReadCompressedFileInPackage(string name)
        {
            string fileContent = string.Empty;

            var assembly = typeof(LocalFilesHelper).GetTypeInfo().Assembly;

            var resources = assembly.GetManifestResourceNames();

            var resourceName = assembly.GetManifestResourceNames().Where(r => r.ToLowerInvariant().EndsWith(name.ToLowerInvariant(), StringComparison.Ordinal)).FirstOrDefault();

            if (resourceName != default(string))
            {
                using (Stream fileStream = assembly.GetManifestResourceStream(resourceName))
                {
                    
                    using (MemoryStream ms = new MemoryStream())
                    {
                        fileStream.CopyTo(ms);

                        var decompressed = Helpers.CompressionHelper.DecompressToMemory(ms.ToArray());

                        fileContent = Encoding.UTF8.GetString(decompressed, 0, decompressed.Length);
                        fileContent = fileContent.Remove(0, 1);
                    }
                }
            }


El auxiliar de descompresión es el siguiente

public static byte[] DecompressToMemory(byte[] fileToDecompress)
        {
            byte[] result = null;
            try
            {
                using (MemoryStream source = new MemoryStream(fileToDecompress))
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        using (GZipStream decompressionStream = new GZipStream(source, CompressionMode.Decompress))
                        {
                            decompressionStream.CopyTo(ms);
                        }

                        result = ms.ToArray();
                    }
                }
            }
            catch (Exception e)
            {
                var a = 0;
            }

            return result;
        }

 

Leer y guardar archivos dentro del paquete de la app

Otro escenario con el que me he encontrado es guardar algunos archivos dentro del paquete de la app durante la ejecución, esto se puede hacer usando los métodos dentro de la clase “System.IO.File”

Una forma  de obtener una ruta valida para almacenar documentos, es utilizando la siguiente línea (funciona en Android, iOS y Windows sin problemas)

static string DEFAULTPATH = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);

Por ejemplo para almacenar bytes este sería el mecanismo ( me ha sido útil para almacenar fotografías por ejemplo)

static string DEFAULTPATH = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);

public static void SaveBytes(string fileName, byte[] data)
        {
            var filePath = Path.Combine(DEFAULTPATH, fileName);
            if (!File.Exists(filePath))
                File.Delete(filePath);
            File.WriteAllBytes(filePath, data);
        } 

Y otro muy común que he usado (por ejemplo para almacenar un json) es este método que guarda texto en un archivo


static string DEFAULTPATH = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);

public static void SaveText(string fileName, string data)
        {
            var filePath = Path.Combine(DEFAULTPATH, fileName);
            if (File.Exists(filePath))
                File.Delete(filePath);
            File.WriteAllText(filePath, data);
        } 


Sus contrapartes para la lectura son las siguientes métodos.

Para leer los arreglo de bytes almacenados podemos usar esta lógica


public static byte[] ReadBytes(string fileName)
        {
            var filePath = Path.Combine(DEFAULTPATH, fileName);
            if (!File.Exists(filePath))
                return null;
            return File.ReadAllBytes(filePath);
        } 


Y para obtener el texto de un archivo sería así


public static string ReadText(string fileName)
        {
            var filePath = Path.Combine(DEFAULTPATH, fileName);
            if (!File.Exists(filePath))
                return null;
            return File.ReadAllText(filePath);
        } 


 

Creo que con esto métodos ya es mas fácil comenzar a entender como funciona el manejo de archivos con las clases propias de .NET y a partir de aquí comenzar a crear algunos escenarios más complejos.

Por ejemplo, el último reto al que me enfrente, relacionado con el tema del post, fue exportar una base de datos SQLite y una serie de fotografías tomadas en la app; el resultado era  un .zip que él usuario podía sacar del teléfono de forma manual. Básicamente es un mecanismo para casos extremos donde no sea posible sincronizar la información a través de la internet.

Y aunque utilice algunas clases extra a las vistas aquí, la base fue muy parecida,

 

¿Tú con que escenarios te has encontrado?

¿Cuál otro te gustaría que pusiera en el post? (Que vaya relacionado al manejo de archivos locales claro 🙂 )


Humberto Jaimes
Humberto Jaimes

Me gusta ayudar a quienes están comenzando con el desarrollo móvil con Xamarin dando sesiones en línea, presenciales en universidades y también ayudo a las compañías que quieren capacitar a su equipo o en la creación de proyectos.

Leave a Reply

Your email address will not be published.