Minitip: Crear y descargar un archivo CSV desde un servicio REST con Spring y Vue.js

Descargarse un archivo usando un servicio REST debería ser sencillísimo e inmediato, porque es una funcionalidad que se usa constantemente en el mundo real. Pero aún así, cada vez que me veo en la situación de tener que implementarla, me atasco.

En este post se va a explicar (o más que explicar, pegar el código de cómo se hace):
– Cómo sacar datos dispersos de la BBDD usando Spring Data
– Cómo montarlos en un csv y devolverlos en un servicio REST, en un proyecto montado con Spring Boot
– Cómo descargarlo desde el front, que está hecho en Vue.js

O sea, todo java y javascript.

Para sacar los datos, si necesitas un informe que tire de diferentes tablas, con una query compleja y cuyo resultado no sea un @Entity, basta con usar una query nativa en el repositorio.

public interface SomeRepository extends CrudRepository {

@Query(value = "/** some complex query **/", nativeQuery = true)
List findAllSomeOutputClassLines(/** some params **/);

La clase resultado SomeOutputClass , en este ejemplo, no se corresponde con ninguna tabla, y tiene una clave compuesta:

@Entity
@Data
@IdClass(SomeOutputClassId.class)
public class SomeOutputClass{
@Id
private SomeType someField;
@Id
private SomeType2 someField2;
...
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class SomeOutputClassId implements Serializable {
private SomeType someField;
private SomeType2 someField2;
}

Al llamar al repositorio, al hacer pruebas he visto que si defino el objeto donde se almacenan los datos como List, la mitad de los campos no se rellenan, se quedan a null. Así que defino el objeto como List:


List responseData = someRepository .findAllSomeOutputClassLines(...);

Hasta aquí ya tenemos los datos, esto es la parte sencilla. El punto de entrada del servicio se definirá de la siguiente forma:


@GetMapping(path = "/somePath", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public @ResponseBody byte[] someFunction(...) {

Montar el csv y devolverlo con la response del servicio:


try {
CSVPrinter csvPrinter = new CSVPrinter(new FileWriter("answers.csv"), CSVFormat.EXCEL.withHeader(/** headers in String separated by commas **/));
for (Object[] data : responseData) {
csvPrinter.printRecord(Arrays.asList(data[0], ... ,data[n]));
}

csvPrinter.flush();

File downloadFile = new File("answers.csv");
InputStream in = new FileInputStream(downloadFile);
return IOUtils.toByteArray(in);

} catch (IOException ex) {
return null;
}

Con Vue.js, recoger la response y guardarla como un csv se haría, usando axios:


this.axios({
url: '/somePath',
method: 'GET',
responseType: 'blob'
}).then((response) => {
var fileURL = window.URL.createObjectURL(new Blob([response.data]))
var fileLink = document.createElement('a')
fileLink.href = fileURL
fileLink.setAttribute('download', 'file.csv')
document.body.appendChild(fileLink)
fileLink.click()
}).catch(function (error) {
console.error(error)
})

Y con esto ya estarían conectados el front y el back, y se podría descargar como csv el archivo con los datos sacados de base de datos.

************************************************************

Espero que esta entrada pueda ser de utilidad, y si no, como siempre, aquí tenéis un gato para compensar.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.