5

I want to implement file download using this Angular 6 code:

Rest API:

private static final Logger LOG = LoggerFactory.getLogger(DownloadsController.class);

private static final String EXTERNAL_FILE_PATH = "/Users/test/Documents/blacklist_api.pdf";

@GetMapping("export")
public ResponseEntity<InputStreamResource> export() throws IOException {
    ClassPathResource pdfFile = new ClassPathResource(EXTERNAL_FILE_PATH);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
    headers.add("Pragma", "no-cache");
    headers.add("Expires", "0");

    return ResponseEntity.ok().headers(headers).contentLength(pdfFile.contentLength())
            .contentType(MediaType.parseMediaType("application/pdf"))
            .body(new InputStreamResource(pdfFile.getInputStream()));
}

Service:

import {Component, OnInit} from '@angular/core';
import {DownloadService} from "../service/download.service";
import {ActivatedRoute, Router} from "@angular/router";
import {flatMap} from "rxjs/internal/operators";
import {of} from "rxjs/index";
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-download',
  templateUrl: './download.component.html',
  styleUrls: ['./download.component.scss']
})
export class DownloadComponent implements OnInit {

  constructor(private downloadService: DownloadService,
              private router: Router,
              private route: ActivatedRoute) {
  }

  ngOnInit() {   
  }

  export() {               
    this.downloadService.downloadPDF().subscribe(res => {
      const fileURL = URL.createObjectURL(res);
      window.open(fileURL, '_blank');
    });         
  } 
}

Component:

import {Component, OnInit} from '@angular/core';
import {DownloadService} from "../service/download.service";
import {ActivatedRoute, Router} from "@angular/router";
import {flatMap} from "rxjs/internal/operators";
import {of} from "rxjs/index";
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-download',
  templateUrl: './download.component.html',
  styleUrls: ['./download.component.scss']
})
export class DownloadComponent implements OnInit {

  constructor(private downloadService: DownloadService,
              private router: Router,
              private route: ActivatedRoute) {
  }

  ngOnInit() {   
  }

  export() {               
    this.downloadService.downloadPDF().subscribe(res => {
      const fileURL = URL.createObjectURL(res);
      window.open(fileURL, '_blank');
    });         
  } 
}

When I click download button I get this Spring error:

15:28:02,819 ERROR [org.springframework.boot.web.servlet.support.ErrorPageFilter] (default task-1) Forwarding to error page from request [/downloads/export] due to exception [class path resource [Users/test/Documents/blacklist_api.pdf] cannot be resolved to URL because it does not exist]: java.io.FileNotFoundException: class path resource [Users/test/Documents/blacklist_api.pdf] cannot be resolved to URL because it does not exist
    at deployment.test_admin.war//org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:195)

The file is present in the directory but looks like it's located outside of the war package and Wildly I can't access it. Is there any way to configure Java to access it and download it?

JustinKSU
  • 4,875
  • 2
  • 29
  • 51
user1285928
  • 1,328
  • 29
  • 98
  • 147
  • Note from the documentation for [`ClassPathResource(path)`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/ClassPathResource.html#ClassPathResource-java.lang.String-): *A leading slash will be removed, as the ClassLoader resource access methods will not accept it.* – MTCoster Nov 10 '18 at 13:59
  • So what should I change? – user1285928 Nov 10 '18 at 14:21
  • I think the larger problem here is that `ClassPathResource` is cannot reference arbitrary parts of the filesystem - it can only find resources from within your current classpath. In this instance, it's also unnecessary - why not just use [`FileInputStream(name)`](https://docs.oracle.com/javase/8/docs/api/java/io/FileInputStream.html#FileInputStream-java.lang.String-)? – MTCoster Nov 10 '18 at 14:25

2 Answers2

4

Since you are using ClasspathResource, you can only get files "within" the classpath.

If your file is outside of the classpath you can't get it in that way.

private static final Logger LOG = LoggerFactory.getLogger(DownloadsController.class);

private static final String EXTERNAL_FILE_PATH = "/Users/test/Documents/blacklist_api.pdf";

@GetMapping("export")
public ResponseEntity<InputStreamResource> export() throws IOException {
    File pdfFile = Paths.get(EXTERNAL_FILE_PATH).toFile();

    HttpHeaders headers = new HttpHeaders();
    headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
    headers.add("Pragma", "no-cache");
    headers.add("Expires", "0");

    return ResponseEntity.ok().headers(headers).contentLength(pdfFile.length())
            .contentType(MediaType.parseMediaType("application/pdf"))
            .body(new FileInputStream(pdfFile));
}

I changed the way you take the file, for a File and not a ClassPathResource.

I modified that piece of code on the fly, sorry if there's any mistake. I hope it can help

bipe15
  • 111
  • 1
  • 8
  • I get The method contentLength() is undefined for the type File – user1285928 Nov 10 '18 at 14:42
  • Change it for the method length() – bipe15 Nov 10 '18 at 16:04
  • I get error `Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]` – user1285928 Nov 10 '18 at 16:59
  • You might have to set in your getMapping your produceType with something like @GetMapping(path="export", produces="application/pdf") – bipe15 Nov 10 '18 at 17:46
  • Anyway you have an example how to download a pdf file from a REST in this [link](https://stackoverflow.com/questions/16652760/return-generated-pdf-using-spring-mvc/16656382#16656382) – bipe15 Nov 10 '18 at 17:53
  • Unfortunately I get again Could not find acceptable representation. Any idea where I'm wrong? – user1285928 Nov 10 '18 at 18:16
0
public static final String TEMPLATE_NAME = "filename.csv";

@Value("templates/" + TEMPLATE_NAME)
private ClassPathResource TEMPLATE;

@GetMapping(value = "/download/template", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void getTemplate(HttpServletResponse response) throws IOException {
    response.setHeader("Content-Disposition", "attachment; filename=" + TEMPLATE_NAME);
    IOUtils.copy(TEMPLATE.getInputStream(), response.getOutputStream());
    response.flushBuffer();
}
JustinKSU
  • 4,875
  • 2
  • 29
  • 51