Angular frontend Spring backend file download not working or corrupt

206 views Asked by At

I have some files (.odt) stored in a mysql database which I try to download

Spring backend (not Spring boot): Controller:

@RequestMapping(value = "/downloadRequest", method = RequestMethod.GET)
    public ResponseEntity<Resource> downloadRequest(@RequestParam(value="id") int id) throws JsonProcessingException {
        try {
            byte [] fileData = getDocumentRestService().downloadRequest(id);
            
            Resource inputStreamResource = new InputStreamResource(new ByteArrayInputStream(fileData));
        
            HttpHeaders headers = new HttpHeaders();
            headers.setContentLength(fileData.length);
            headers.set(HttpHeaders.CONTENT_TYPE, "application/vnd.oasis.opendocument.text");
            headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + inputStreamResource.getFilename() + "\"");
            
            ResponseEntity<Resource> responseEntity = ResponseEntity.ok().headers(headers).body(inputStreamResource);
            return responseEntity;
        } catch (Exception e) {
            return null;
        }
    }

Angular: Component:

download() {
        let id: number = this.documentDetailFormGroup.controls['id'].value;

        if(id > 0) {
            this.documentDatasource.downloadFormData(id).subscribe(data => {
                let fileData = [];
                fileData.push(data.body);


                let downloadLink = document.createElement('a');
                downloadLink.href = window.URL.createObjectURL(new Blob(data, { type: 'application/vnd.oasis.opendocument.text' }));
                downloadLink.download = "Test.odt";
                document.body.appendChild(downloadLink);
                downloadLink.click();
                document.body.removeChild(downloadLink);
            });
        }
    }

GenericDatasource:

downloadFormData(id: number): Observable<any> {
        return this.ssBaseRequestService.sendDownloadRequestWithParams("GET", this.requestMapping + "/downloadRequest", this.ssBaseRequestService.httpParams(id));
    }

RequestService:

public sendDownloadRequestWithParams(method: string, url: string, params?: HttpParams): Observable<any> {
       return this.httpClient.get(this.backend_url + url, {params: params, observe: 'response', responseType: 'blob'});
    }

For Spring I already tried returning byte[], Response, and json response with Jackson:

//     console.log(data.dto.fileData);
                //     var buf = Buffer.from(data.dto.fileData, 'base64').toString('utf8');
                //     console.log(buf);
                //     //console.log(atob(data.dto.fileData));
                //     let fileData = [];
                // //    let fileData = new Blob(atob(data.dto.fileData), { type: data.dto.contentType });
                //    fileData.push(buf);
                //     // console.log(fileData);

For Angular I also tried returning:

Observable<Blob> 

Nothing seems to work.
Either the .odt file is empty or the .odt file contains [Object object], sometimes it's not opening in Libreoffice (corrupted)

The byte[] in the backend contains some negative numbers, which are converted to positive numbers (+256) in the frontend, causing it to be corupted?

Anyone any idea what i'm doing wrong?

Also tried: Angular Spring Boot File Download

1

There are 1 answers

0
KurtJ On

After searching for a couple of days I think I finally found the problem. In the backend I was doing, to conver the blob:

public static byte[] convertBlobToBytes(Blob blob) {
        if(blob == null) {
            throw new ServiceException("exists.not", new String[] {"file"});
        }
        
        try {
            StringWriter stringWriter = new StringWriter();
            IOUtils.copy(blob.getBinaryStream(), stringWriter, Encoding.UTF_8);
            return stringWriter.toString().getBytes();
        } catch (SQLException e) {
            throw new ServiceException("exists.not", new String[] {"file"});
        } catch (IOException e) {
            throw new ServiceException("filecopy.failed");
        }
    }

I replaced it with:

public static byte[] convertBlobToBytes1(Blob blob) {
        if(blob == null) {
            throw new ServiceException("exists.not", new String[] {"file"});
        }
        
        try {
            return blob.getBytes(1, (int)blob.length());
        } catch (SQLException e) {
            throw new ServiceException("exists.not", new String[] {"file"});
        }
    }

And that seems to do the trick.