52

I wrote code that generate Excel file using REST JAX-RS and I confirmed that the generated Excel file is in GlassFish server directory.

But my goal is when user click on the button (which generate Excel .xls), I want download popup to show up asking user whether to save or open the .xls file just like any other web services doing for downloading any type of files.

According to my search, the step is:

  1. generate Excel .xls (DONE)

  2. write the excel to stream

  3. in JAX-RS file, set response header to something like,

    String fileName = "Blah_Report.xls"; response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

My question is I'm doing all of this in JAX-RS file and I don't have HttpServletResponse object available.

According to the answer from Add Response Header to JAX-RS Webservice

He says:

You can inject a reference to the actual HttpServletResponse via the @Context annotation in your webservice and use addHeader() etc. to add your header.

I can't really figure what exactly that means without sample code..

Community
  • 1
  • 1
Meow
  • 18,371
  • 52
  • 136
  • 180

3 Answers3

97

You don't need HttpServletResponse to set a header on the response. You can do it using javax.ws.rs.core.Response. Just make your method to return Response instead of entity:

return Response.ok(entity).header("Content-Disposition", "attachment; filename=\"" + fileName + "\"").build()

If you still want to use HttpServletResponse you can get it either injected to one of the class fields, or using property, or to method parameter:

@Path("/resource")
class MyResource {

  // one way to get HttpServletResponse
  @Context
  private HttpServletResponse anotherServletResponse;

  // another way
  Response myMethod(@Context HttpServletResponse servletResponse) {
      // ... code
  }
}
keisar
  • 5,266
  • 5
  • 28
  • 28
Tarlog
  • 10,024
  • 2
  • 43
  • 67
  • @GarretWilson Why not? What does confuse you? It's a simple header. – Tarlog Dec 25 '14 at 15:02
  • Because a singleton `MyResource` would have `myMethod()` called by multiple threads simultaneously. Each thread would have a different instance of `HttpServletResponse`, yet the singleton `anotherServletResponse` member variable can only hold a single value at a time. The only way this would work is if `anotherServletResponse` were injected with some thread-safe proxy that uses a thread local or some such to determine the actual `HttpServletResponse` instance of the current thread. – Garret Wilson Dec 25 '14 at 15:16
  • As far as I remember HttpServletRequestWrapper is injected; its implementation takes the real HttpServletRequest from the TLS. So your guess is more or less correct. – Tarlog Dec 25 '14 at 15:23
  • 1
    This is very awesome! It also [answered](http://stackoverflow.com/a/27648777/421049) another question I had. This information is hard to find; few pages on the web mention member injection, and many lists of injectable types for singletons are incomplete. Thanks Tarlog!! – Garret Wilson Dec 25 '14 at 16:11
  • @Tarlog: please surround filename with double quotes, otherwise, chrome will reject files with commas in filename – Denis Tulskiy Feb 18 '15 at 04:41
  • Feel free to edit my response, if you feel that it is needed to be improved. – Tarlog Feb 18 '15 at 08:21
17
@Context ServletContext ctx;
@Context private HttpServletResponse response;

@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Path("/download/{filename}")
public StreamingOutput download(@PathParam("filename") String fileName) throws Exception {
    final File file = new File(ctx.getInitParameter("file_save_directory") + "/", fileName);
    response.setHeader("Content-Length", String.valueOf(file.length()));
    response.setHeader("Content-Disposition", "attachment; filename=\""+ file.getName() + "\"");
    return new StreamingOutput() {
        @Override
        public void write(OutputStream output) throws IOException,
                WebApplicationException {
            Utils.writeBuffer(new BufferedInputStream(new FileInputStream(file)), new BufferedOutputStream(output));
        }
    };
}
Jay
  • 671
  • 9
  • 10
1

I figured to set HTTP response header and stream to display download-popup in browser via standard servlet. note: I'm using Excella, excel output API.

package local.test.servlet;

import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import local.test.jaxrs.ExcellaTestResource;
import org.apache.poi.ss.usermodel.Workbook;
import org.bbreak.excella.core.BookData;
import org.bbreak.excella.core.exception.ExportException;
import org.bbreak.excella.reports.exporter.ExcelExporter;
import org.bbreak.excella.reports.exporter.ReportBookExporter;
import org.bbreak.excella.reports.model.ConvertConfiguration;
import org.bbreak.excella.reports.model.ReportBook;
import org.bbreak.excella.reports.model.ReportSheet;
import org.bbreak.excella.reports.processor.ReportProcessor;

@WebServlet(name="ExcelServlet", urlPatterns={"/ExcelServlet"})
public class ExcelServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        try {

            URL templateFileUrl = ExcellaTestResource.class.getResource("myTemplate.xls");
            //   /C:/Users/m-hugohugo/Documents/NetBeansProjects/KogaAlpha/build/web/WEB-INF/classes/local/test/jaxrs/myTemplate.xls
            System.out.println(templateFileUrl.getPath());
            String templateFilePath = URLDecoder.decode(templateFileUrl.getPath(), "UTF-8");
            String outputFileDir = "MasatoExcelHorizontalOutput";

            ReportProcessor reportProcessor = new ReportProcessor();
            ReportBook outputBook = new ReportBook(templateFilePath, outputFileDir, ExcelExporter.FORMAT_TYPE);

            ReportSheet outputSheet = new ReportSheet("MySheet");
            outputBook.addReportSheet(outputSheet);

            reportProcessor.addReportBookExporter(new OutputStreamExporter(response));
            System.out.println("wtf???");
            reportProcessor.process(outputBook);


            System.out.println("done!!");
        }
        catch(Exception e) {
            System.out.println(e);
        }

    } //end doGet()

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

}//end class



class OutputStreamExporter extends ReportBookExporter {

    private HttpServletResponse response;

    public OutputStreamExporter(HttpServletResponse response) {
        this.response = response;
    }

    @Override
    public String getExtention() {
        return null;
    }

    @Override
    public String getFormatType() {
        return ExcelExporter.FORMAT_TYPE;
    }

    @Override
    public void output(Workbook book, BookData bookdata, ConvertConfiguration configuration) throws ExportException {

        System.out.println(book.getFirstVisibleTab());
        System.out.println(book.getSheetName(0));

        //TODO write to stream
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment; filename=masatoExample.xls");
            book.write(response.getOutputStream());
            response.getOutputStream().close();
            System.out.println("booya!!");
        }
        catch(Exception e) {
            System.out.println(e);
        }
    }
}//end class
Meow
  • 18,371
  • 52
  • 136
  • 180