Hi, I wanted to return a file from a resteasy server. For this purpose, I have a link at the client side which is calling a rest service with ajax. I want to return the file in the rest service. I tried these two blocks of code, but both didn't work as I wanted them to.
#POST
#Path("/exportContacts")
public Response exportContacts(#Context HttpServletRequest request, #QueryParam("alt") String alt) throws IOException {
String sb = "Sedat BaSAR";
byte[] outputByte = sb.getBytes();
return Response
.ok(outputByte, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = temp.csv")
.build();
}
.
#POST
#Path("/exportContacts")
public Response exportContacts(#Context HttpServletRequest request, #Context HttpServletResponse response, #QueryParam("alt") String alt) throws IOException {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=temp.csv");
ServletOutputStream out = response.getOutputStream();
try {
StringBuilder sb = new StringBuilder("Sedat BaSAR");
InputStream in =
new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
byte[] outputByte = sb.getBytes();
//copy binary contect to output stream
while (in.read(outputByte, 0, 4096) != -1) {
out.write(outputByte, 0, 4096);
}
in.close();
out.flush();
out.close();
} catch (Exception e) {
}
return null;
}
When I checked from the firebug console, both of these blocks of code wrote "Sedat BaSAR" in response to the ajax call. However, I want to return "Sedat BaSAR" as a file. How can I do that?
Thanks in advance.
There're two ways to to it.
1st - return a StreamingOutput instace.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download() {
InputStream is = getYourInputStream();
StreamingOutput stream = new StreamingOutput() {
public void write(OutputStream output) throws IOException, WebApplicationException {
try {
output.write(IOUtils.toByteArray(is));
}
catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"temp.csv\"").build();
}
You can return the filesize adding Content-Length header, as the following example:
return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition", "attachment; filename=\"temp.csv\"").header("Content-Length", getFileSize()).build();
But if you don't want to return a StreamingOutput instance, there's other option.
2nd - Define the inputstream as an entity response.
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download() {
InputStream is = getYourInputStream();
return Response.code(200).entity(is).build();
}
Related
I have two services returning two different ResponseEntity.
public ResponseEntity<InputStreamResource> getA(...) {
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.contentLength(out.size())
.body(new InputStreamResource(bis)); }
public ResponseEntity<InputStreamResource> getB(...) {
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.contentLength(out.size())
.body(new InputStreamResource(bis)); }
Each service has a controller that calls and returns.
public ResponseEntity<InputStreamResource> getA(...) {
return aService.getA(...) }
public ResponseEntity<InputStreamResource> getB(...) {
return bService.getB(...) }
I'm trying to create another controller which does and return both services at once.
public ResponseEntity<InputStreamResource> getAB(...) {
return aService.getA(...) *and* bService.getB(...) ?????? }
not sure how to combine two ResponseEntities returns into one.
Returning ResponseEntity from a Service method is not a good idea.
It's the Controller layer which should be responsible for generating ResponseEntity Object. That is his business not of the Service Layer. Similarly, the responsibility of the Service Layer is to prepare some kind of a DTO object based on the given input and then the Controller will wrap around that DTO and send it as a response.
So, I suggest to do some structural change here.
Service Layer
public InputStreamResource getA(...) {
return A
}
public InputStreamResource getB(...) {
return B
}
Controller Layer
public ResponseEntity<InputStreamResource> getA(...) {
return new ResponseEntity<>(aService.getA(...) (
}
public ResponseEntity<InputStreamResource> getB(...) {
return new ResponseEntity<>(bService.getB(...) )
}
To merge 2 streams
If you are targeting to stream 2 different pdf documents one by one, then I think the option will be to merge pdf documents in memory first with whatever pdf library you might be using. Then create a single InputStreamResource as Response.
But, If the streams can be run in sequence, then below is a working example to merge 2 streams using SequenceInputStream -
#RequestMapping(
path = "/sayHello",
method = RequestMethod.GET,
produces = MediaType.TEXT_PLAIN_VALUE
)
public ResponseEntity<InputStreamResource> get() {
byte[] inputBytes1 = "Hello".getBytes();
// 1st stream has "Hello" text
ByteArrayInputStream baos1 = new ByteArrayInputStream(inputBytes1);
byte[] inputBytes2 = "World".getBytes();
// 2nd stream has "World" text
ByteArrayInputStream baos2 = new ByteArrayInputStream(inputBytes2);
// combined stream will have "HelloWorld" text
SequenceInputStream combinedStream = new SequenceInputStream(baos1, baos2);
InputStreamResource inputStreamResource = new InputStreamResource(combinedStream);
return ResponseEntity.ok().body(inputStreamResource);
}
Output ->
curl -X GET http://localhost:8083/sayHello
HelloWorld
You can try via this way:
public ResponseEntity<List<InputStreamResource> getAAndB(...) {
private List<InputStreamResource> result = new ArrayList<>();
result.add(aService.getA(...));
result.add(bService.getB(...));
return ResponseEntity.ok().body(result)); }
}
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
#RestController
public class ReportController {
private static final Logger LOGGER = ...
#Autowired
private ReportManager manager;
#GetMapping("/reportcard/students")
public ResponseEntity<InputStreamResource> getStudentReportCard(parameters...) throws ServiceException {
List<InputStream> studentReportList = manager.getStudentReports(parameters.... );
HttpHeaders headers = getHeaders(Constants.STUDENTS_REPORT_CARD);
Document document = new Document(PageSize.LETTER);
ByteArrayOutputStream outputStream = null;
try {
outputStream = new ByteArrayOutputStream();
PdfCopy copy = new PdfCopy(document, outputStream);
document.open();
for (InputStream file : studentReportList) {
copy.addDocument(new PdfReader(file)); // writes directly to the output stream
}
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document.isOpen()) {
document.close();
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
InputStreamResource inputStreamResource = new InputStreamResource(new ByteArrayInputStream(outputStream.toByteArray()));
return ResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_PDF).body(inputStreamResource);
}
private HttpHeaders getHeaders(String fileName) {
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.set("Content-disposition", "attachment; filename=" + fileName);
return headers;
}
public class ReportManagerImpl implements ReportManager {
private static final Logger LOGGER = ...
#Autowired
private HttpClient httpClient;
#Override
public List<InputStream> getStudentReports(parameters...)
throws ServiceException {
List<InputStream> studentReportList = new Vector<InputStream>();
List<String> learnersList = //list of students
String reportUrl = //url
for (String learner : learnersList) {
studentReportList.add(getHttpResponse(reportUrl + learner));
}
return studentReportList;
}
private InputStream getHttpResponse(String url) throws ServiceException {
try {
HttpResponse response = httpClient.execute(new HttpGet(url));
HttpEntity entity = response.getEntity();
return entity.getContent();
} catch (IOException e) {
throw new ServiceException(e.getMessage());
}
}
I am trying to create a proxy server in java using Spark Java.
I listed for any incoming request and send any incoming request to the target server via Apache Http Client.
I then transform the incoming request by coping the headers, status and text.
Now this is working pretty fine, except the binary files. The binary files such as fonts are corrupted. My code is :
private static void mapStatus(HttpResponse response, spark.Response res) {
res.status(response.getStatusLine().getStatusCode());
}
private static void mapHeaders(HttpResponse response, spark.Response res) {
for (Header header : response.getAllHeaders()) {
res.header(header.getName(), header.getValue());
}
}
private static String result(HttpResponse response) throws ParseException, IOException {
HttpEntity entity = response.getEntity();
return entity == null ? "" : EntityUtils.toString(entity);
}
I suspect the issue is with the result() method as it treats all the responses as text. How ever I tried by coping the streams as is :
private static void extractResponse(HttpResponse httpResponse, HttpServletResponse response) {
InputStream inputStream = null;
try {
HttpEntity entity = httpResponse.getEntity();
if(entity == null)
return;
inputStream = entity.getContent();
copyStream(inputStream, response.getOutputStream());
} catch (IllegalStateException | IOException e) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
private static void copyStream(InputStream input, OutputStream output) throws IOException {
IOUtils.copy(input, output);
}
And the binary files are still bad.
WHat could be the issue here ?
Complete Code Link
I have a Servlet which makes a request to my Rest API, and I want it to return the API Response content to the final user through the HttpServletResponse.
The content is actually a .xls file to download which I put in the Response with the StreamingOutput Object.
How can I do that ? I can't cast the Response into a HttpServletResponse
Rest API method :
#GET
#Produces( MediaType.APPLICATION_JSON )
#Path("bla")
public Response getTopicByName() {
final Workbook wb = new HSSFWorkbook();
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream output) throws IOException, WebApplicationException {
wb.write(output);
}
};
responseBuilder = responseBuilder.entity(stream);
responseBuilder = responseBuilder.status(Response.Status.OK);
responseBuilder = responseBuilder.header("Content-Disposition", "attachment; filename=" + device + ".xls");
return responseBuilder.build();
}
Servlet POST method :
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
Response res = target. request().get();
if (res.getStatus() == 200) {
// how to put res stream into response stream ?
ServletOutputStream stream = response.getOutputStream();
}
client.close();
}
EDIT :
I tried TedTrippin method and after finding out the way to recover an InputStream from the Response, it worked well.
But I keep getting corrupted xls files. And it is quite annoying. I don't get those corrupted files when I make the request directly from the browser.
Got any clues where it comes from ?
POST method :
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(url + param + format);
Response res = target.request().get();
if (res.getStatus() == 200) {
response.setHeader("Content-Disposition", "attachment; filename=test.xls");
InputStream in = res.readEntity(InputStream.class);
ServletOutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
while (in.read(buffer) >= 0) {
out.write(buffer);
}
out.flush();
}
client.close();
}
Simplest way is to read the response stream and write it straight to the response output stream. Either use a library function from IOUtils or Guava or pure java...
try (InputStream in = ...;
OutputStream out = ...) {
byte[] buffer = new byte[1024];
while (in.read(buffer) >= 0)
out.write(buffer);
} catch (IOException ex) {
...
}
A nicer (depending on your view) way would be to read/save the response as a temporary file then you could return that or write it to the output stream.
Third approach would be to create a pipe, but I don't think that would be applicable here.
My Excel file seems to be generated but the function doesn't return anything:
#RequestMapping(value = "/excel", method = RequestMethod.POST, consumes = APPLICATION_JSON, produces = "application/vnd.ms-excel")
public void generateExcelExport(#RequestBody String rawContentParameters, final HttpServletRequest request, final HttpServletResponse response) throws FunctionalError, TechnicalError, IOException {
for (JsonNode personNode : rootNode) {
if (personNode instanceof ObjectNode) {
ObjectNode object = (ObjectNode) personNode;
object.remove("reportKey");
}
}
rawContentParameters = rootNode.toString();
ReportParameter reportParameters = new ReportParameter(reportCode);
HSSFWorkbook workbook = null;
try {
workbook = exportExcelService.getFile(reportParameters, rawContentParameters);
} catch (TechnicalError e1) {
redirectToErrorPage(request, response, rawContentParameters, Constants.ERR_BAD_REQUEST);
}
try {
if (workbook != null) {
workbook.write(response.getOutputStream());
}
response.flushBuffer();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=stuff");
}
}
What's wrong with it? Even though the return parameter is void, I read that the response should make the download possible.
You are just setting response headers and not sending the response.
Here is a link which sends a any file as response.
http://balusc.blogspot.de/2007/07/fileservlet.html
UPDATE:
Here is how you can write to response directly from HSSWorkbook. So replace the last part with the following.
HSSFWorkbook wb = getWorkbook(); // I think you already have a workbook
OutputStream out = response.getOutputStream();
try {
wb.write(out);
}
catch (IOException ioe) {
// handle exception
}
method = RequestMethod should be GET.
I'm not sure where or what command to use to add the HTTP header to the response from the server.
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
public class Response {
private static final int BUFFER_SIZE = 9999;
Request request;
BufferedOutputStream output;
//constructor para el output
public Response(BufferedOutputStream output){
this.output = output;
}
//Set del request
public void setRequest(Request request){
this.request = request;
}
public void sendResource() throws IOException{
File file = new File(Java_Server.Web_dir,request.getUri());
byte [] bytearray = new byte[(int) file.length()];
FileInputStream file_out = null;
if(file.exists())
file_out = new FileInputStream(file);
else{
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
BufferedInputStream bis = new BufferedInputStream(file_out);
try{
bis.read(bytearray,0,bytearray.length);
output.write(bytearray,0 , bytearray.length);
output.flush();
output.close();
return;
}catch (IOException e){
e.printStackTrace();
}
}
The contents is deliver to the browser but without the HTTP header and if a image is send for example, the browser doesn't show the image, it shows byte for byte.
The preferred way to do is, is to implement a Servlet and run it in a Servlet Container. Then you call the method setHeader on the HttpServletResponse object:
public class ExampleServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setHeader("X-Whatever-Header-Name-You-Want", "Value");
}
}