Quantcast

File Transfer Ionic | Aquí tienes la guía más completa de la red

Aitor Sánchez - Blog - Oct. 27, 2023, 10:39 a.m.

¿Necesitas que tu usuario pueda enviar, o recibir, archivos por internet desde tu app? O, quizás, ya sabes cómo hacerlo y solo te falta algún dato sobre alguna función o campo de FileTransfer en Ionic ¿verdad?

Mira,

Mi nombre es Aitor Sánchez, soy desarrollador de apps desde el 2014, y en el artículo de hoy te enseñaré, paso a paso, cómo puede descargar y enviar archivos desde tu aplicación mediante una conexión http y la librería File Transfer de Ionic. Así que pilla sitio que empezamos...

Pero antes de continuar, esta es la Flutter Mafia. Es mi newsletter donde aprenderás desarrollo de apps móviles, aso y monetización junto con otros genietes que ya están dentro. Y si te suscribes te regalo mi ebook "Duplica los ingreso de tus apps en 5 minutos" No es broma, quizás te interese.

 

Cómo instalar File Transfer

Como con todos los componentes externos al core que vas a utilizar, tienes que instalar tanto el plugin para la comunicación nativa, como el código de abstracción para que puedas enviar y recibir información desde tu código TypeScrypt.

$ ionic cordova plugin add cordova-plugin-file-transfer
$ npm install --save @awesome-cordova-plugins/file-transfer

 

En la primera línea, instalas el plugin de cordova para la comunicación nativa con el dispositivo de tu usuario.

La segunda línea es para instalar las dependencias y que puedas, desde tu código TS, acceder a la entrada y salida del módulo desde tu código.

Y para terminar con la instalación necesitarás, en tu archivo appmodule, incluir dentro de los provider el componente File Transfer de la siguiente manera:

Nota: Recuerda que, si usas una versión superior a Ionic 3, y el módulo NGX, esto no es necesario que lo hagas.

 

import { FileTransfer } from '@awesome-cordova-plugins/file-transfer/ngx';

...

@NgModule({
  ...
  //Aquí es donde tenemos que meter la clase para que la podamos usar.
  providers: [
    ...
    FileTransfer
    ...
  ]
  ...
})
export class AppModule { }

 

Configurando nuestra librería de archivos

Vale, menos trabajo a realizar. Este módulo, como muchos otros, no necesita una configuración previa. Así que te ahorras este paso. ¿Qué guay no? ;)

 

Ejemplo/example de FileTransfer para subir y descargar de internet

Vale, aquí empieza lo bueno. Te enseño un ejemplo, que en la mayoría de las ocasiones es bastante más ilustrativo.

Usaré un código de ejemplo que te servirá perfectamente para subir (upload), o bajar (download), contenido a una api mediante File Transfer.

Aquí vamos:

import { FileTransfer, FileUploadOptions, FileTransferObject } from '@awesome-cordova-plugins/file-transfer/ngx'; //Si usas NGX
import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer'; //Si no usas NGX
import { File } from '@ionic-native/file';

//Iyectamos la clase en el componente y se lo asignamos al campo de clase transfer
//El file es necesario para poder enviar el archivo solamente, en futuros tutoriales hablaremos del esta clase.
constructor(private transfer: FileTransfer, private file: File) { }

...

//Creamos la instancia del fileTransfer a transfer con un voley.
const fileTransfer: FileTransferObject = this.transfer.create();

// Subida del archivo.
fileTransfer.upload(..).then(..).catch(..);

// Descarga del archivo.
fileTransfer.download(..).then(..).catch(..);

// Esta función aborta la transferencia en curso.
fileTransfer.abort();

// Ejemplo completo
upload() {
  let options: FileUploadOptions = {
     fileKey: 'file',
     fileName: 'name.jpg',
     headers: {}
     .....
  }

  fileTransfer.upload('<file path>', '<api endpoint>', options)
   .then((data) => {
     // success
   }, (err) => {
     // error
   })
}

download() {
  const url = 'http://www.example.com/file.pdf';
  fileTransfer.download(url, this.file.dataDirectory + 'file.pdf').then((entry) => {
    console.log('download complete: ' + entry.toURL());
  }, (error) => {
    // Controlamos el error aquí.
  });
}

 

UoUo, cuanto código, madre mía. No pensaba que fuera a salir tanto… Una cosa más, si en algún momento te hace falta subir contenido desde la web, este código también te sirve para AngularJS en gran medida, quizás no todo, pero gran parte sí.

En primer lugar tienes que importar los componentes "FileTransfer", "FileUploadOptions", "FileTransferObject" y "File" de las librerías "@Ionic-native/file-transfer" y "@Ionic-native/file".

Ahora, como era de esperar, tienes que hacer las inyecciones de dependencias en tu clase.

Las que vas a inyectar son: FileTransfer y File. Las otras que he nombrado arriba son interfaces necesarias para el uso y no es necesario que las cargues en el constructor.

No voy a explicar más código que el que hemos explicado ya. He intentado ser lo más ilustrativo posible y creo que es suficiente.

Nota: Este tuto necesita una dependencia que aún no está descrita. Para poder almacenar un archivo descargado dentro de nuestro teléfono necesitamos saber usar la gestión de almacenamiento en Ionic. Pero tranquilo, hare un tutorial dentro de muy poco.

Es más, pondré aquí un enlace para que lo veas directamente (Si el enlace aún no está, es que el tuto no está realizado aún. Así que sí necesitas esta información ya, en el blog, al menos de momento, no la vas a encontrar, lo siento :P).

Te dejo un enlace a la página oficial mientras cocino el tutorial: https://ionicframework.com/docs/native/file/

 

Contenido programable disponible en File Transfer

Ahora vamos a ver más información sobre los componentes que hemos usado para llevar a cabo este ejemplo y que te quede todo más o menos claro.

Es posible que mientras vayas programando la app, te des cuenta de que el sistema de permisos (permissions) de lectura/escritura de archivos no funcione cómo esperas. Para darle solución lo que tienes que hacer es pedir los permisos de manera explícita al usuario en el momento que los necesitemos.

Se hace así:

this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE).then( (result) => {
        if (result.hasPermission) {
          // Nuestro código
        } else {
          this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE).then(result => {
            if (result.hasPermission) {
              // code
            }
          });
        }
      },
      err => this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
    );

Sigamos...

Esta clase cuenta con dos miembros imprescindibles para poder hacer funcionar todo el tinglado al pie de la letra.

  • FileTransferErrorCode -> Te vale para identificar que está pasando en caso de error.
  • create() -> La función principal para poner en marcha la maquinaria. Te devuelve un “FIleTransferObject” que es el objeto con el que vamos a trabajar.

Ahora vamos a ver las clases adicionales que hemos usado

  • FileTransferObject -> Encargado de la lógica de las subidas y las bajadas.
    • Upload(fileUrl, Url, Options, trustAllHost) -> Función encargada de subir los contenidos al servidor.
      • Parámetros ->
        • fileUrl -> string -> Es la ruta del documento que queremos subir interna de nuestro dispositivo.
        • url -> string -> La url a la que queremos enviar los datos.
        • fileUploadOptions -> FIleUploadOptions -> Parámetros adicionales que queremos enviar con la petición.
        • trustAllHost -> bolean -> Parámetro adicional para para compatibilizar la petición con host https. Por defecto false y no se recomienda su uso en producción.
      • Respuesta ->
        • Esta función retorna una promesa que en el “Then” trae un FileUploadResult con los datos del estado de la subida.

 

  • Download(source, target, trustAllHost, optional) -> Encargada de descargar archivos desde el server con la clase File Transfer Ionic.
    • Parámetros->
      • Source -> string -> La url del servidor del que queremos descargar el documento.
      • Target -> string -> La ruta en el dispositivo donde queremos guardar el archivo. (Es necesario el path absoluto)
      • trustAllHost -> bolean -> Para la compatibilidad de servidores con cifrado https.
      • Option -> FileUploadOptions -> Información adicional que queremos enviar con la petición.
    • Respuesta ->
      • Promesa que contiene los datos que se envían desde el servidor.

 

  • onProgress(listener) -> Nos va devolviendo el estado de la descarga para que podamos, por ejemplo, rellenar una barra de progreso.
    • Parámetros ->
      • Listener -> Una función anónima que tiene los datos de progreso en los parámetros.

 

  • Abort() -> Aborta la descarga del documento en cuestión.

 

  • FileUploadOptions -> Este objeto contiene las opciones de configuración cuando llamamos a “upload” o “download”.
    • fileKey -> string -> Este será el key del documento que vamos a enviar cuando lo recuperamos desde, por ejemplo, PHP con $_FILE [“<fileKey>”]
    • fileName -> string -> El nombre del archivo cuando es recuperado desde el servidor.
    • httpMethod -> string -> El método que vamos a usar para enviar el documento (post, get, path, put, etc…)
    • mimeType -> string -> El mimeType es para decirle al servidor que tipo de contenido le vamos a enviar (Insertar enlaces del mime tipe) aquí puedes ver los tipos.
    • Params -> {<key>:<value>} -> Digamos que esto nos sirve para enviar información adicional en la petición.
    • chunkedMode -> boolean -> Esto es importante, nos permite decirle al servidor si vamos a enviar un contenido concreto o vamos a enviar contenido variable en forma de Stream.
    • Headers -> {<key>:<value>} -> Similar a params pero para las cabeceras de la petición.

 

  • FileUploadResult -> Este es el resultado que nos llega cuando llamamos al método download.
    • bytesSent -> number -> El número de bytes que han sido enviados.
    • responseCode -> number -> El código de respuesta que nos aporta el servidor cuando intentamos realizar la conexión.
    • response -> string -> La respuesta que nos ha ofrecido el servidor.
    • headers -> {<key>:<value>} -> Las cabeceras que nos ha enviado el servidor.

 

  • FileTransferError -> Este objeto contiene el resultado que nos ofrece el sistema cuando en los métodos “download” y “upload” sale por el catch.
    • code -> number -> El código de error que nos ofrece el sistema Ionic para este tipo de objectos.
    • source -> string -> La ruta de la petición.
    • target -> string -> La ruta del dispositivo en la que vamos a almacenar el archivo.
    • http_status -> number -> El código de estado que nos ha devuelto el servidor sobre la petición.
    • body -> string -> El cuerpo de la respuesta que nos ha dado el servidor.
    • exception -> string -> Contiene el mensaje de error que nos ha dado el sistema.

 

Cómo usar File Transfer Ionic en uploads en Base64

Bueno, quizás esto daría para algo más que un capítulo por muchos motivos. Pero el principal es, que no se puede. He estado mirando a ver si estaba equivocado por todo internet, pero no, no se puede usar con Base64 por lo menos hasta el momento.

Me explico mejor. FileTransfer no utiliza contenido almacenado en ram, hace uso de contenidos almacenados en de manera persistente en dispositivo. Esto es lo que manda y recibe.

Básicamente, pilla el archivo, le saca los Bytes y después los manda mediante un multipart/form-data para enviarlos. Y para descargar, igual que lo hace todo el mundo, lee el buffer de entrada y lo va escribiendo en un archivo local.

No vamos a parar a explicar aquí cómo funciona la tecnología Blob, daría para un tuto entero, pero vamos, que no se puede realizar.

 

Cómo enviar imágenes (image) directamente desde Camera con Ionic y FileTransfer

Vale, esta parte me habéis pedido que la aclarase más. Pues bien, aquí vamos.

En las aplicaciones móviles todo lo que tenga que ver con la cámara es demandado ya de por si. Entonces, una de las cosas que no se puede quedar fuera es enseñaros cómo se puede enviar el contenido a server directamente después de tomar una foto. Pues lo tenemos que hacer de la siguiente manera:

En primer lugar, vamos a programar una función para tomar la imagen desde la cámara. Si aún no sabes cómo se usa la cámara, aquí tienes nuestro tutorial para aprender a utilizarla cómo un pro :)

pickPicture(){
  Camera.getPicture({
      destinationType: Camera.DestinationType.DATA_URL,
      sourceType     : Camera.PictureSourceType.PHOTOLIBRARY,
      mediaType: Camera.MediaType.PICTURE
  }).then((imageData) => {
    // imageData es una cadena codificada en base64
      this.base64Image = "data:image/jpeg;base64," + imageData;
  }, (err) => {
      console.log(err);
  });
}

 

Ahora podemos hacer dos cosas.

  • Enviar el archivo mediante FileTransfer cómo hemos visto en los ejemplos, pasándole la ruta que acabamos de recuperar al sistema de subida.
  • O, podemos enviar la imagen tal cual mediante una petición http normal cómo vamos a ver ahora:

 

this.http.post("http://nuestroserver.com", this.base64Image)
    .map((res:Response) => res.json())
    .catch((error:any) => Observable.throw(error.json().error || 'Server error'));

Bien, tendríamos que codificar en la parte de arriba el parse para crear el archivo. Pero bueno, ahí está. Se puede de las dos maneras.

Pero si tuviese que elegir una, me quedo con la primera. Básicamente por que enviar el archivo entero, tienes un retorno con datos, en PHP se controlar desde los $_FILE, y vamos que se hace cómo toda la vida. Por lo menos desde que yo llevo programando :)

 

La descarga (download) me retorna un 401 al usar FileTransfer

Este es un error bastante común en las compilaciones en Android. El porqué, es muy sencillo. Es por que nos falta que instalar el plugin WhiteList. Se hace de la siguiente manera:

$ cordova plugin add cordova-plugin-whitelist

 

Y posteriormente tienes que agregar esto en archivo xml de configuración:

<access origin="*" subdomains="true" />
<allow-navigation href="http://*/*" />
<allow-navigation href="https://*/*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />

 

Te explico. En Android, en las últimas versione de SO, han agregado un sistema de seguridad para que el dispositivo no permita enviar peticiones http fuera de unos dominios que tenemos que definir en un archivo. Vamos que, si no se ha especificado el dominio ahí, no te va a dejar tirar una query a ese dominio, o sub-dominio.

 

Enviar el archivo con más datos

Está bien, esto es un problema que llevo arrastrando bastante tiempo. Yo lo sufro más porque al programar en nativo es un poco más complejo, pero el hecho de enviar un archivo mediante FileTransfer con más parámetros (with data) se debe hacer de la siguiente manera:

var uploadOptions = {
      fileKey: "file", // Es la KEY desde la que se va a recuperar el archivo sobre el server.
      chunkedMode: false, // Agregar chunkedMode, sirve para decir si la petición va dividida o en un bloque.
      mimeType: "multipart/form-data", // Agregamos el mimeType para enviar archivos.
      fileName: "img.png",
      params : {'bid':businessId,"imgurl":theFile,"content":content},      
      headers: {'Authorization':'Bearer ' + val, 'Content-Type': 'application/x-www-form-urlencoded'}
    };

Si te fijas, tenemos que configurar las opciones cómo si se tratasen de un formulario en HTML. Con este ejemplo ya te va a funcionar sin problemas :P

Una cosa más antes de seguir. Se me ha consultado cómo podemos definir un TimeOut sobre la petición. Pues bien, imagino que están definidos de manera interna. La verdad que nunca había hecho por preguntarme esta cuestión, pero ahora que lo preguntan, pues tiene sentido... El sistema no cuenta con una manera de definir el TimeOut de la petición de manera manual. Así que tendremos que confiar en lo que haga por debajo...

 

Cómo enviar múltiples archivos (multiple files) con FileTransfer

En primer lugar, quiero aclarar que el sistema no permite realizar en envío de varios archivos en la misma petición. Entonces, cómo estarás imaginando, tenemos meterlo todo dentro de un bucle e ir uno por uno. La respuesta, al tratarse de una promesa, será enviado todo a la vez en lugar de secuencialmente.

import { FileTransfer, FileUploadOptions, FileTransferObject } from '@awesome-cordova-plugins/file-transfer/ngx';

 uploadAllImage()
  {
    const fileTransfer:FileTransferObject = this.transfer.create();

    var i;

    var filetype;
    var itemtype;

    for(i=0; i<this.photos.length; i++)
    {

      //Obtener el tipo de archivo.
      filetype = this.itemtypes[i];

      //Obtenemos el mime type dependiendo del tipo recuperado antes.
      switch(filetype)
      {
        case 'audio':
        {
          itemtype = 'audio/amr';
          break;
        }
        case 'video':
        {
          itemtype = 'video/quicktime';
          break;
        }
        case 'image':
        {
          itemtype = 'image/jpeg';
          break;
        }
        default:
        {
          return;
        }
      }


      //Fijamos el nombre del archivo.
      var name ='pinglun_' + filetype;
      name = name + '#';
      name = name + this.photosName[i];

      //Seteamos el mime type de la petición.
      let option: FileUploadOptions = {
        fileKey:'file',
        mimeType:itemtype,
        httpMethod:'POST',
        fileName:name
      };

      if(filetype == 'image')
      {
        fileTransfer.upload(this.photos[i], encodeURI(localStorage.getItem('mi_dominio') + "/upload"),option).then((result)=>{

        },(error) => {

        });
      } else {
        fileTransfer.upload(this.fileurls[i], encodeURI(localStorage.getItem('mi_dominio') + "/upload"),option).then((result)=>{

        }, (error) => {

        });
      }
    }
  }

Pues aquí tienes un buen ejemplo que podemos usar para llevar a cabo la subida múltiple.

 

¿Y si no me funciona (not working) el FileTransfer qué hago?

Bien, después de mucho rebuscar. Y ver los errores reportados por los usuarios por la red, y las posibles soluciones, me decanto por una. Y es esta:

Primer vamos a actualizar todo lo interno de Ionic. Tranqui que la versión en sí, no se actualiza con esto.

npm i @ionic/app-scripts@latest --save
npm i ionic-native@latest --save

 

Y posteriormente tenemos que obtener la instancia de FileTransfer de la siguiente manera:

this.platform.ready().then(() => {
  // Okay, so the platform is ready and our plugins are available.
  // Here you can do any higher level native things you might need.
  this.fileTransfer = this.transfer.create();
});

 

La diferencia está en crearla aquí en lugar de, por ejemplo, el constructor. Y con esto ya te debería de funciona.

 

Un ejemplo más completo en vídeo

 

Algo más que quizás te interese

La optimización del logo debe ser una de la patas principales en tu estrategia de ASO. Mejor logo, más descargas, más pasta. Hasta ahí bien. ¿Cómo lo puedes hacer? Pues mira, hemos creado para ti una herramienta que evalua y te da consejos sobre cómo mejorar tu logo. Aquí tienes más detalles.

 

Y ahora si, nos vemos en el siguiente artículo. Hasta entonces ¡que vaya bien!

Otros artículos que te pueden interesar

Ionic Keyboard | Con ejemplos claros y sencillos

¿Necesitas recoger información del usuario, la lógica del teclado natural no...

Firebase en Ionic - Qué es y cómo puedes utiliz...

¿Pensando en integrar Firebase dentro de tu app hecha en Ionic? O, quizás, ya lo ha...

Ionic Bluetooth | Qué es y cómo tu puedes utili...

¿Quieres envair y recibir información con otro dispositivo desde tu app hecha en Io...

Ion-radio (Ionic radio button) de 0 a 100

¿Pensando en incluir radio buttons en tu app de Ionic? o, tal vez, solo quieres conocer m&...

Ion Select | todo lo que debes saber está aquí

¿Necesitas que tu usuario seleccione un valor de una lista y has pensado en ion-select? O,...

Ionic Slider con ejemplos claros y prácticos

¿Quieres hacer un step by step, o montar un carrousel de imágenes en tu app? O, tal...