Download de qualquer arquivo - resolvido

component

download(idArquivo: number): void {
    this.arquivosService.downloadArquivoOrcamento(idArquivo).subscribe(
      data => {
        const blob = new Blob([data.arquivo], { type: 'text/json; charset=utf-8' });
        const url= window.URL.createObjectURL(blob);
        window.open(url);
      },
      err => {
        console.error(err);
      }
    )
  }

Service

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { SERVER_API_URL } from 'app/app.constants';
import { Observable } from 'rxjs';
@Injectable({
    providedIn: 'root'
})
export class ArquivosService {
    constructor(
        private http: HttpClient
    ) {}
    downloadArquivoOrcamento(id: number, tipo: string): Observable<any> {
        return this.http.get(SERVER_API_URL + '/api/orcamento-anexo/' + id, 
        {
            responseType: 'blob',
            headers: new HttpHeaders().append('Content-Type', tipo)
        });
    }
}

Retorno do backend


Não retorna o arquivo conforme imagem anterior

Dowload com erro

O que pode ser ?

Alguma ajuda ?

Alguma ajuda ?

Tu consegue imprimir no console os dados do arquivo que são retornados do serviço?

Consigo.

Mas não seria o que está na imagem Retorno do backend ?

1 curtida

Falta de atenção minha. Aquele conteudo do atributo arquivo é um base64, certo?

Se for, vc pode seguir os passos desse link que vc deve conseguir: https://medium.com/@riccardopolacci/download-file-in-javascript-from-bytea-6a0c5bb3bbdb

Da forma vc vc está fazendo, funcionaria se vc estivesse retornando o arquivo sem ser na base64.

Como teria que retornar ?

Retorna os bytes do arquivo apenas. Mas como base64 tb funciona, soh que vc teria que fazer uma conversão (conforme o link que mandei - tem a ver com a função atob).

Retorna uma arquivo corrompida.

Este é a classe que retorna com as informações do arquivo

public class ArquivosDTO {

	private Long id;
	private String nome;
	private String contentType;
	private byte[] arquivo;
	private Long tamanho;
}

Ao fazer o download, ele abre assim.

Será que o problema está no envio dos arquivos ?

Meu incluir está assim:

service

incluir(orcamento: IOrcamentoIncluirModel, files: File[]): Observable<EntityResponseType> {
    const orcamentoIncluir = 'dto';
    const filesServer = 'files';
    const formData: FormData = new FormData();
    if(files !== undefined) {
      for (let i = 0; i < files.length; i++) {
        const blob: Blob = new Blob([JSON.stringify(orcamento)], {type: 'application/json'});
        formData.append(orcamentoIncluir, blob);
        formData.append(filesServer, files[i]);
      }
    }
    return this.http
      .post<IOrcamentoIncluirModel>(this.api + 'incluir', formData, { observe: 'response' })
      .pipe(map((res: EntityResponseType) => this.convertDataParaServidor(res)));
}

Java

endpont

@PostMapping(value = "incluir")
@PreAuthorize("hasAuthority('" + ADMINISTRADOR + "') ")
public ResponseEntity<?> incluir(@Valid @RequestPart OrcamentoIncluirDTO dto,
	@RequestPart Collection<MultipartFile> files) {
	try {
		log.info("Metodo que inclui um orcamento");
		OrcamentoDTO orcamento = service.incluir(dto, files);
		log.info("Com o id: " + orcamento.getId());
		orcamento.setMensagem(mensagemSalvar(false));
		return new ResponseEntity<>(orcamento, OK);
	} catch (Exception e) {
		log.error(e.getMessage(), e);
		throw new BadRequestAlertException(e.getMessage(), ORCAMENTO, null);
	}
}

Serviço

private Collection<OrcamentoAnexo> ajustesAnexos(Collection<MultipartFile> anexos, Orcamento orcamento) {
		log.info("Metodo que faz ajustes em anexos para o orçamento");
		Collection<OrcamentoAnexo> anexosBanco = new ArrayList<>();
		if (anexos != null && !anexos.isEmpty()) {
			anexos.forEach(anexo -> {
				OrcamentoAnexo anexoBanco;
				try {
					anexoBanco = OrcamentoAnexo.builder().tamanho(anexo.getSize()).arquivo(anexo.getBytes())
							.nome(anexo.getOriginalFilename()).orcamento(orcamento).contentType(anexo.getContentType())
							.build();
					anexosBanco.add(anexoBanco);
					aplicacaoAuditoria(anexoBanco);
				} catch (IOException e) {
					log.error(e.getMessage(), e);
				}
			});
			log.info("Total de anexos: " + anexos.size());
		}
		return anexosBanco;
	}

No Java a não ser o anexo.getBytes(), o restante está chegando correto.

No angular alterei e ficou assim:

download(idArquivo: number, tipo: string): void {
     this.arquivosService.downloadArquivoOrcamento(idArquivo).subscribe(
       (data: any) => {
        const blob = new Blob([data.arquivo], { type: tipo });
        const fileName = data.nome;
        if (navigator.msSaveBlob) {
         // IE 10+
         navigator.msSaveBlob(blob, fileName);
       } else {
         const link = document.createElement('a');
         // Browsers that support HTML5 download attribute
         if (link.download !== undefined) {
           const url = URL.createObjectURL(blob);
           link.setAttribute('href', url);
           link.setAttribute('download', fileName);
           link.style.visibility = 'hidden';
           document.body.appendChild(link);
           link.click();
           document.body.removeChild(link);
         }
       }
       },
       err => {
         alert("Problem while downloading the file.");
         console.error(err);
       }
     )
   }

Essa é uma classe que uso para fazer download de base64:

export default class DownloadUtils {
  static MYME_TYPE_PDF = 'application/pdf';

  static baixarDocumentoBase64Pdf(conteudo, nomeArquivo) {
    DownloadUtils.baixarDocumentoBase64(conteudo, nomeArquivo, DownloadUtils.MYME_TYPE_PDF);
  }

  static baixarDocumentoBase64(conteudo, nomeArquivo, mymeType) {
    const blob = DownloadUtils.b64toBlob(conteudo, mymeType);
    const blobUrl = URL.createObjectURL(blob);

    const downloadLink = document.createElement('a');
    downloadLink.href = blobUrl;
    downloadLink.download = nomeArquivo;
    downloadLink.click();
    downloadLink.remove();
  }

  static b64toBlob(b64Data, contentType) {
    const sliceSize = 512;
    const byteCharacters = atob(b64Data);
    const byteArrays = [];

    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
      const slice = byteCharacters.slice(offset, offset + sliceSize);

      const byteNumbers = new Array(slice.length);
      for (let i = 0; i < slice.length; i += 1) {
        byteNumbers[i] = slice.charCodeAt(i);
      }

      const byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }

    return new Blob(byteArrays, { type: contentType });
  }
}

O segredo está na função b64toBlob, que vai converter o base64 para array de bytes e assim criar o blob corretamente.

1 curtida

Todos testes que fiz funcionaram com todas as extensões.

Obrigado @Lucas_Camara

1 curtida