es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Flujo a OutputStream

Estoy descargando un archivo de una API REST y quiero pasarlo a mi aplicación Angular. Entonces, una función de descarga.
Actualmente, el archivo descargado se escribe en un archivo utilizando DataBufferUtils y luego se pasa al flujo de salida de la respuesta.

Pero ahora quiero que los datos de Flujo vayan directamente al flujo de salida y no se almacenen en caché.

La siguiente implementación tenía el problema de que la carpeta ZIP deseada se descarga pero, desafortunadamente, vacía.

public Flux<databuffer> descargarArchivos(OAuth2AuthorizedClient authorizedClient, String id) {
    return get(uri).attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))
            .retrieve().bodyToFlux(DataBuffer.class);
}
dataBufferFlux = orderWebService.descargarArchivos(authorizedClient, id);
DataBufferUtils.write(dataBufferFlux, response.getOutputStream());

Angular:

return this.http.get(uri, {
  observe: 'response',
  responseType: 'blob',
  withCredentials: true
})
  .pipe(map(res => {
      const contentDisposition = res.headers.get('Content-Disposition');
      const filename = this.getFilenameFromContentDisposition(contentDisposition);

      return {
        body: new Blob([res.body], {type: res.headers.get('Content-Type')}),
        filename: filename
      }
    })
  ).subscribe(response => {

  let binaryData = [];
  binaryData.push(response.body);
  var url = window.URL.createObjectURL(new Blob(binaryData, {type: "application/pdf"}));
  var a = document.createElement('a');
  document.body.appendChild(a);
  a.setAttribute('style', 'display: none');
  a.setAttribute('target', 'blank');
  a.href = url;
  a.download = response.filename;
  a.click();
  window.URL.revokeObjectURL(url);
  a.remove();

}, error => {

  console.log(error);
});

¡Agradezco cualquier ayuda e inspiración!

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para pasar los datos de Flux directamente al flujo de salida sin almacenamiento en caché, puedes utilizar el método writeWith() proporcionado por Spring WebFlux. Este método te permite escribir un Publisher directamente en el flujo de salida subyacente sin almacenamiento en búfer o en caché.

    Aquí tienes un ejemplo de implementación:

    public Mono<void> downloadFile(ServerHttpResponse response) {
        // crear un Flux<databuffer> a partir del archivo descargado
        Flux<databuffer> dataBufferFlux = downloadFileFromApi();
    
        // configurar los encabezados de respuesta y el estado
        response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
        response.getHeaders().setContentDisposition(ContentDisposition.attachment().filename("nombre_archivo.zip").build());
        response.setStatusCode(HttpStatus.OK);
    
        // escribir los datos directamente en el flujo de salida sin almacenamiento en caché
        return response.writeWith(dataBufferFlux);
    }
    

    En tu aplicación Angular, puedes utilizar la opción responseType de HttpClient para recibir la respuesta como un blob. Luego puedes crear una URL para el blob y descargarlo utilizando una etiqueta de anclaje.

    Aquí tienes un ejemplo de implementación:

    downloadFile() {
      const url = 'http://localhost:8080/descargar';
      return this.http.get(url, {responseType: 'blob'})
        .pipe(map(res => {
          // extraer el nombre de archivo de los encabezados de respuesta
          const filename = this.getFilenameFromContentDisposition(res.headers.get('Content-Disposition'));
          return {blob: res, filename: filename};
        }))
        .subscribe(res => {
          // crear URL para el blob y descargarlo utilizando una etiqueta de anclaje
          const blobUrl = URL.createObjectURL(res.blob);
          const a = document.createElement('a');
          document.body.appendChild(a);
          a.setAttribute('style', 'display: none');
          a.href = blobUrl;
          a.download = res.filename;
          a.click();
          URL.revokeObjectURL(blobUrl);
          a.remove();
        });
    }
    
    private getFilenameFromContentDisposition(contentDisposition: string): string {
      const matches = /filename\*=UTF-8''([\w%]+\.[\w]{3,4})/i.exec(contentDisposition);
      if (matches !== null) {
        return decodeURIComponent(matches[1]);
      }
      return 'desconocido';
    }
    

    Ten en cuenta que el método getFilenameFromContentDisposition() se utiliza para extraer el nombre de archivo del encabezado Content-Disposition enviado por el servidor. Este método maneja tanto nombres de archivo codificados en ASCII como en UTF-8.

Comments are closed.