Saltar al contenido

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

File Transfer Ionic

En el tuto de hoy vamos a hablar de lo que es el File Transfer en Ionic y de cómo podemos enviar y recibir documentos desde internet de la manera más cómoda y rápida posible. Con documentos me refiero a imágenes, pdfs, words, etc… cualquier tipo de contenido (en archivo) que se nos ocurra. Visto esto, comencemos.

Nota: es importante saber que han marcado a FileTransfer cómo deprecated. Pero vamos, que yo lo voy a seguir usando porque es muy sencillo y fácil de usar. Por lo menos hasta que pueda ser una amenaza usarlo o lo des-compatibilicen.

Eyyyy! Muy buenas majo lector y guapa lectora. Aitor a los mandos de ComoProgramar.net otra vez. Cómo has podido leer en el texto de presentación, vamos a hablar de uno de los componentes que más se utilizan en aplicaciones, que, por regla general, trabajan de manera offline. Como puede ser un toDoList, un diccionario, un álbum, etc… vamos, cualquier app que necesite descargar un archivo de manera remota y guardarlo.

Pero antes de continuar, déjame decirte que llevo ya un tiempo trabajando con los chicos de Raiola Netwoks, son proveedores de Hosting, y estoy muy contento con ellos. He aumentado el rendimiento de mis webs y apis más de un 30% en comparación con 1&1, que es donde los tenía antes. Si estás pensando en cambiar, o pillar uno nuevo, te los recomiendo bastante. Aquí tienes un enlace con más info. Solo espero que te puedan ayudar igual que lo han hecho conmigo. Ahora sí, continuemos…

¿Qué, te mola la idea? ¿Te quedas conmigo hasta el final? ¡Nah!, apuesto a que sí, no te llevará más de 10 minutillos entender el contenido de este artículo.

Instalar File Transfer

Como con todos los componentes externos al core que vayamos a utilizar, tenemos que instalar tanto el plugin para la comunicación nativa, como el código de abstracción para poder enviar y recibir información en nuestro código TypeScrypt.

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

 

En la primera línea instalamos el plugin de cordova para, como he comentado un poco más arriba, la comunicación nativa con nuestro dispositivo.

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

Y para terminar con la instalación necesitamos, en nuestro archivo appmodule, incluir dentro de los provider nuestro 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 '@ionic-native/file-transfer';

...

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

 

Configuración de nuestro módulo

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

 

Ejemplo/example de File Transfer para subir y descargar de internet

Vale, aquí empieza lo bueno. Veamos un ejemplo, que en la mayoría de ocasiones es bastante más ilustrativo.

Usaremos 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 '@ionic-native/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 tenemos 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, tenemos que hacer las inyecciones de dependencias en nuestra clase. Las que vamos a inyectar son: FileTransfer y File. Las otras que hemos nombrado arriba son interfaces necesarias para el uso y no es necesario que las carguemos en el constructor.

No vamos a explicar más código que el que hemos explicado ya porque he intentado ser lo más ilustrativo posible en el código. ¿Qué manera te gusta más? ¿Qué te lo explique después? ¿o mejor sobre el código? Dependiendo de la respuesta a esta pregunta en futuros tutos lo haré a votación popular. Así todos ganamos 🙂

Nota muy importante: Este tuto necesita una dependencia que aún no está escrita. Para poder almacenar un archivo descargado dentro de nuestro teléfono necesitamos sabes usar la gestión de almacenamiento en Ionic. Pero tranquilo, hare un tutorial dentro de muy poco ¿OK? Es más, podré 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). Pero como soy buena persona, aquí te dejo un enlace donde puede encontrar mucha información sobre esto: https://ionicframework.com/docs/native/file/

 

Contenido programable

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 vayamos programando la app, nos demos cuenta de que el sistema de permisos (permissions) de lectura/escritura de archivos no funcione cómo esperamos. Pues para darse solución lo único que tenemos 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)
    );

Y ahora sí, sigamos…

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

  • FileTransferErrorCode -> Nos vale para identificar que está pasando en caso de error.
  • create() -> La función principal para poner en marcha la maquinaria. Nos 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

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 😛

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 '@ionic-native/file-transfer';

 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/[email protected] --save
npm i [email protected] --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

Bueno lector/a, ya hemos llegado al final del artículo. Solo queda una cosa, despedirme y decirte que si te interesa forma parte del círculo, una comunidad online que estoy montando alrededor de esta disciplina, te pases por aquí. Todos los detalles están dentro.

Un saludo y nos vemos en el siguiente artículo.