angular - Capturar y subir imagen al servidor usando Ionic 4
He estado intentando capturar una imagen y subirla al servidor durante días, pero sin suerte. Estoy usando Ionic 4 para mi lado del cliente, y Java como mi backend (usé Jersey para exponer mi backend a REST).
Ahora, el problema es que después de tomar la imagen y tratar de subirla, sigo recibiendo un valor nulo en mi backend.
Aquí está mi código del lado del cliente:
openCam(){
const options: CameraOptions = {
quality: 100,
destinationType: this.camera.DestinationType.FILE_URI,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
correctOrientation: true,
cameraDirection: 1
}
this.camera.getPicture(options).then((imageData) => {
// imageData is either a base64 encoded string or a file URI
// If it's base64 (DATA_URL):
//alert(imageData)
this.imageData = imageData;
this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
this.isImageCaptureed = true;
}, (err) => {
// Handle error
alert("error "+JSON.stringify(err))
});
}
upload(){
let url = 'http://mydommain/api/upload';
let dataURL = 'data:image/jpeg;base64,' + this.imageData;
let postData = new FormData();
postData.append('file', dataURL);
let data:Observable<any> = this.http.post(url,postData);
data.subscribe((result) => {
console.log(result);
});
}
Intenté pasar imageData directamente al objeto FormData, también intenté convertirlo usando la función DataURIToBlob() como encontré en algún otro problema similar, pero aún así no tuve suerte..
dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
Sé que el problema está solo en el formato imageData. Dado que logré seleccionar un archivo usando la etiqueta de entrada HTML y subirlo usando la misma función upload() mencionada arriba y mi API de backend.
Respuesta #1
Aquí hay una manera detallada de subir imágenes capturadas tomadas por Ionic 4 al servidor backend.
Supongamos que tenemos la siguiente clase CaptureImage dedicada a capturar y subir la imagen tomada
export class CaptureImage implements OnInit {
constructor() { }
ngOnInit() {
}
}
Primero, necesitamos instalar @ionic-native/camera usando :
ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera
puedes referirte a la documentación de Ionic
Entonces necesitas declarar un objeto de cámara en tu clase, así que nuestra clase se convertirá en:
import { Camera, CameraOptions } from '@ionic-native/camera/ngx';
export class CaptureImage implements OnInit {
//image to be displayed in template
image;
imageData;
constructor(private camera: Camera) { }
ngOnInit() {
}
}
A continuación necesitamos un disparador en nuestra plantilla, para poder llamar a una función que capture la imagen, por lo que la plantilla (capture-image.page.html) se verá algo así:
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button (click)="capture()">
<ion-icon ios="ios-camera" md="md-camera"></ion-icon>
</ion-fab-button>
</ion-fab>
Observa que definimos un evento de clic que llamaba a la función capture(). Ahora necesitamos implementar esta función.
Así que volviendo a nuestra clase CaptureImage, aquí está la función capture():
openCamera(){
const options: CameraOptions = {
quality: 100,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
}
this.camera.getPicture(options).then((imageData) => {
this.imageData = imageData;
this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
}, (err) => {
// Handle error
alert("error "+JSON.stringify(err))
});
}
Tenga en cuenta que el tipo de destino debe ser this.camera.DestinationType.DATA_URL
¡Felicidades! Has tomado la foto, puedes ver la foto tomada en tu plantilla usando:
<img [src]="image" >
ahora necesitamos subirlo al servidor. Para mayor simplicidad, implementaré la función de subida directamente en la clase CaptureImage. pero en realidad es mejor implementar todas las llamadas al backend en un servicio dedicado, y luego inyectarlo en tu clase.
Entonces, para subir la imagen, necesitaremos nuevamente un desencadenador, así que en nuestra plantilla, definamos un botón de subida:
<ion-button (click)="upload()" color="success">
<ion-icon slot="icon-only" name="checkmark"></ion-icon>
</ion-button>
Entonces, de vuelta en la clase CaptureImage, primero vamos a inyectar el HttpClient en el constructor
import { HttpClient } from '@angular/common/http';
export class CaptureImage implements OnInit {
//image to be displayed in template
image;
imageData;
constructor(private camera: Camera,
private http: HttpClient){ }
ngOnInit() {
}
}
entonces, definamos la función upload() :
upload(){
let url = 'your REST API url';
const date = new Date().valueOf();
// Replace extension according to your media type
const imageName = date+ '.jpeg';
// call method that creates a blob from dataUri
const imageBlob = this.dataURItoBlob(this.imageData);
const imageFile = new File([imageBlob], imageName, { type: 'image/jpeg' })
let postData = new FormData();
postData.append('file', imageFile);
let data:Observable<any> = this.http.post(url,postData);
data.subscribe((result) => {
console.log(result);
});
}
¡Casi terminado! Todavía necesitamos implementar una función más: dataURItoBlob, esta función crea archivos blob a partir de dataURLI:
dataURItoBlob(dataURI) {
const byteString = window.atob(dataURI);
const arrayBuffer = new ArrayBuffer(byteString.length);
const int8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
int8Array[i] = byteString.charCodeAt(i);
}
const blob = new Blob([int8Array], { type: 'image/jpeg' });
return blob;
}
Finalmente, así es como se verá la clase CaptureImage al final:
import { Observable } from 'rxjs';
import { OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Camera, CameraOptions } from '@ionic-native/camera/ngx';
export class CaptureImage implements OnInit {
//image to be displayed in template
image;
imageData;
constructor(private camera: Camera,
private http: HttpClient) { }
ngOnInit() {
}
openCamera(){
const options: CameraOptions = {
quality: 100,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
}
this.camera.getPicture(options).then((imageData) => {
this.imageData = imageData;
this.image=(<any>window).Ionic.WebView.convertFileSrc(imageData);
}, (err) => {
// Handle error
alert("error "+JSON.stringify(err))
});
}
upload(){
let url = 'your REST API url';
const date = new Date().valueOf();
// Replace extension according to your media type
const imageName = date+ '.jpeg';
// call method that creates a blob from dataUri
const imageBlob = this.dataURItoBlob(this.imageData);
const imageFile = new File([imageBlob], imageName, { type: 'image/jpeg' })
let postData = new FormData();
postData.append('file', imageFile);
let data:Observable<any> = this.http.post(url,postData);
data.subscribe((result) => {
console.log(result);
});
}
dataURItoBlob(dataURI) {
const byteString = window.atob(dataURI);
const arrayBuffer = new ArrayBuffer(byteString.length);
const int8Array = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
int8Array[i] = byteString.charCodeAt(i);
}
const blob = new Blob([int8Array], { type: 'image/jpeg' });
return blob;
}
}
Respuesta #2
Intenta convertir a datos Blob antes de asignarlos al objeto FormData.
const formData = new FormData();
const imgBlob = new Blob([reader.result], {
type: file.type
});
formData.append('file', imgBlob, file.name);
Enlace de referencia: https://devdactic.com/ionic-4-image-upload-storage/
