I have a servlet in an GWT app thats creates a PDF file with the data given with the post request and sends the responst back:
public void service(HttpServletRequest request, HttpServletResponse response) {
try {
String text = request.getParameter("text");
if (text == null || text.trim().length() == 0) {
text = "no data";
}
//PDF Creation with iText
Document document = new Document();
ByteArrayOutputStream b = new ByteArrayOutputStream();
PdfWriter.getInstance(document, b);
document.open();
document.add(new Paragraph(text));
document.close();
response.setHeader("Expires", "0");
response.setHeader("Cache-Control",
"must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setContentType("application/pdf");
response.setContentLength(b.size());
OutputStream os = response.getOutputStream();
b.writeTo(os);
os.flush();
os.close();
} catch (Exception ex) {
System.out.println(ex.toString());
}
}
I want to show the created PDF to the User. I got this far on the client:
final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
GWT.getModuleBaseURL() + "PdfServlet");
rb.setHeader("Content-type", "application/x-www-form-urlencoded");
StringBuffer postData = new StringBuffer();
postData.append(URL.encode("text")).append("=")
.append(URL.encode(Text));
rb.setRequestData(postData.toString());
rb.setCallback(new RequestCallback() {
#Override
public void onResponseReceived(Request request,
Response response) {
if (200 == response.getStatusCode()) {
//What to do here?
} else {
//TODO:Something
}
}
#Override
public void onError(Request request, Throwable exception) {
/TODO:...
}
});
try {
rb.send();
} catch (RequestException e) {
e.printStackTrace();
}
So my question is:
How do I show this PDF to the user?
All i managaged to do is show the pdf with "no data" in it..
Thank you for you help :)
Instead of using a RequestBuilder, you can simply use Window.Location.setUrl(yourDowloadUrl?key=value) and include your parameters in the query String. Note however that you must set the Content-disposition header: attachment header so the browser will prompt you to save or open the file, and not replace your GWT app.
Better even, create a hidden iframe in your html page, and call setUrl on that widget.
The downside of using this approach is that it doesn't allow your client code to capture feedback if something goes wrong server-side and instead of a pdf the call returns HTML with an error string from your web server. If that's very important to you, you should use a polling mechanism that requests the document, which is then produced and saved on the server, and checks every n seconds whether there is something to download. I have implemented something like this, which also prevents timeout issues with large documents. Let me know if you're interested
you should create pdf file from your servlet and stored at somewhere on server. You need to return file path where you stored on the server. And now from GWT you can prompt window to user to download file. Below is the example for downloading file from GWT:
Window.open(GWT.getHostPageBaseURL() + your return path from server, "", "");
I could displayed the created pdf without the need to save the file on the server and keep unique key. It works on Chrome but according to some posts it might be a problems on some old browsers.
Window.open("data:application/pdf;base64," + result, cRFTitle.replace(" ", "_") + ".pdf", "enabled");
As suggested the Result need to be Base64 encoded
B.
Related
Am uploading a file (any format doc, docx, pdf, text, etc) as multipart form/data to a REST API from Postman or application UI. The text file uploads fine. All other non-text formats get corrupted. I cant open those files.
The size of the uploaded file increases drastically. Check the following server log:
File size just before call to request.getRequestDispatcher().forward(): 27583
Controller, first line in method:39439
The size of the uploaded file is 27.3Kb
I am guessing the files gets corrupted because of the other data appended to the file.
Controller method is
#RequestMapping(value="/entity/{entity}/{entityId}/type/{type}/{derive}",method = RequestMethod.POST)
#ResponseBody
public String uploadFile(#RequestParam("file") MultipartFile multipartFile,#PathVariable("entity")String entity,#PathVariable("type")String type,#PathVariable("entityId")Long entityId,#PathVariable("derive") boolean derive) throws Exception
Since text file is saving correctly and other files also get written correctly, don't think the code to write the file is incorrect.
Code to get inputStream
public String storeFile(MultipartFile multipartFile, String entity, Long id, String uploadType, boolean isDerive,String customName)
throws Exception
{
try
{
System.out.println(multipartFile.getSize());
String fileName = "";
String contentType = "";
if (multipartFile != null)
{
fileName = multipartFile.getOriginalFilename();
contentType = multipartFile.getContentType();
if (contentType == null)
{
contentType = "application/msword";
}
}
InputStream is = multipartFile.getInputStream();
String filePath = getFileName(entity, uploadType, id, fileName, isDerive,customName);
Helper.storeFile(is, filePath);
precedingPath = precedingPath.length() > 0 ? precedingPath + "/":"";
return precedingPath + filePath;
}
catch (WebException e)
{
e.printStackTrace();
throw e;
}
catch (Exception e)
{
e.printStackTrace();
throw new WebException(e.getMessage(), IHttpConstants.INTERNAL_SERVER_ERROR, e);
}
}
Helper.storeFile
public static File storeFile(InputStream is, String filePath) throws IOException {
try {
String staticRepoPath = null;
if (MasterData.getInstance().getSettingsMap().containsKey(Settings.REPO_LOCATION.toString())) {
staticRepoPath = MasterData.getInstance().getSettingsMap().get(Settings.REPO_LOCATION.toString());
} else {
throw new WebException("Invalid Settings");
}
byte[] buffer = new byte[is.available()];
is.read(buffer);
File targetFile = new File(staticRepoPath + File.separator + filePath);
#SuppressWarnings("resource")
OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);
return targetFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
My Ajax request is as follows
var fd = new FormData();
//Take the first selected file
fd.append("file", document.actualFile);
//Generic AJAX call
CandidateService.ajax_uploadDocumentWithDocType($scope.candidate.id, fd, document.docType, function (error, json)
Content type while uploading:
var config = {headers:{'X-Auth-Token':authToken, 'Content-Type': undefined}, transformRequest: angular.identity};
Would anyone know how I can fix this and upload the file successfully?
Q1) Why does the file size change between the request dispatcher and the controller that handles the file data.
Q2) Could this change of file size be the cause of file corruption? Libre Office cause General Input/Output Error.
I figured the problem with the file upload. I had a spring filter in between that was changing the request to a wrappedRequest. This was adding additional data to the multipart data and causing the file to be corrupted.
Well in my case I had this exact same problem when accesing the API through Amazon API Gateway. Turned out I forgot to allow multipart ContentType on API Gateway.
Kind of weird the requests still made it to my server and text files worked fine.
I tried to upload a image to the server, it throws me the Error 405 : Method not found, but from that same Url i can able to Download any files.. following is the code i tried.
private void uploadFileToServer(ActionEvent event) throws IOException
{
try{
InfiniteProgress ip = new InfiniteProgress();
Dialog dlg = ip.showInifiniteBlocking();
dlg.show();
MultipartRequest request = new MultipartRequest();
FileSystemStorage fs = FileSystemStorage.getInstance();
String fileUri = fs.getAppHomePath() + "654319032015150536IR.png";
request.setUrl("http://192.XX.XX.58:XX/HttpFolder/");
request.setPost(true);
InputStream is = FileSystemStorage.getInstance().openInputStream(fileUri);
request.addData("file", is, FileSystemStorage.getInstance().getLength(fileUri), "image/png");
request.setFilename("file", fileUri);
request.setPriority(ConnectionRequest.PRIORITY_CRITICAL);
NetworkManager.getInstance().addToQueue(request);
dlg.dispose();
if (event instanceof NetworkEvent) {
NetworkEvent ne = (NetworkEvent)event;
Dialog.show("Result:", ne.getMetaData().toString(), "","");
}
}
catch(Exception e){
Dialog.show("ERROR", e.getMessage(), "OK",null);
}
}
You can't upload to an arbitrary URL, you need to have a servlet that handles multipart on the POST http method.
We have a demo that includes the server side code here: http://codenameone.com/blog/build-mobile-ios-apps-in-java-using-codename-one-on-youtube.html
Notice that you also did some other "problematic" things in the code such as using PRIORITY_CRITICAL and using the InputStream API rather than just giving the file URL (which is more efficient).
Hi have written controller class like below. I am trying to get file from mongo db and try to download it.
organizationFileAttachmentService.setUser(getUser());
GridFSDBFile file = organizationFileAttachmentService.getGridFSDBFileById(new ObjectId(id), "File");
if (file != null) {
byte[] content = organizationFileAttachmentService.findByIdAndBucket(new ObjectId(id), "File");
try {
int size = content.length;
InputStream is = null;
byte[] b = new byte[size];
try {
is = new ByteArrayInputStream(content);
is.read(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (Exception ex) {
}
}
response.setContentType(file.getContentType());
// String attachment =
// "attachment; filename=\""+file.getFilename()+"\"";
String attachment = "attachment; filename=" + file.getFilename();
// response.setContentLength(new
// Long(file.getLength()).intValue());
response.setCharacterEncoding(file.getMD5());
response.setHeader("content-Disposition", attachment);// "attachment;filename=test.xls"
// copy it to response's OutputStream
// FileCopyUtils.copy(is, response.getOutputStream());
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
is.close();
} catch (IOException ex) {
_logger.info("Error writing file to output stream. Filename was '" + id + "'");
throw new RuntimeException("IOError writing file to output stream");
}
but i am not able to down load file. can any one help me.
In case you missed it, Spring provides various built in resource handlers.
http://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/resources.html#resources-implementations
If your method returns one of those (perhaps the ByteArrayResource in your case), then you just need a couple of annotations on the interface like so:
#RequestMapping(value = "/foo/bar/{fileId}",
method = RequestMethod.GET,
produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
#ResponseBody FileSystemResource downloadFile(Long fileId);
No fiddling with encodings and headers for you that way. I'd recommend trying that before rolling your own.
Edit: The above worked fine in Spring 3.1.4. It no longer works for 3.2.x or 4.x. Whereas previously, the produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE } would cause Spring to add the appropriate headers, it now treats that as a restriction. If accessing the URL with a standard web browser, an accept header of "application/octet-stream" will not be sent. Spring will therefore return a 406 error. To get it working again, such a method needs to be re-written without the "produces" attribute. Instead, add HttpServletResponse to the method arguments and add the header inside the method. i.e.:
#RequestMapping(value = "/foo/bar/{fileId}",
method = RequestMethod.GET)
#ResponseBody FileSystemResource downloadFile(
Long fileId, HttpServletResponse response) {
...
response.setHeader( "Content-Disposition", "attachment;filename=" + fileName );
...
}
Edit redux:
Now using Spring 4.0.7 via Spring Boot 1.1.8. It would appear that setting the produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE } instruction is now working again. Just having that instruction seems to be enough for all the browsers I have tried. Note however, that I have also found that it does not set the Content-Disposition, which is left as application/json. Although this doesn't seem to be an issue for browsers, I have come across bugs in PHP client applications, which seem to behave only based on the Content-Disposition. So it seems that the current solution is to do both of the above!
I have changed my request as GET and added request in anchor tag in html. Aslo changed my code as
#RequestMapping(value = "/getFileById/{id}", method = RequestMethod.GET)
public #ResponseBody
void download(#PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
organizationFileAttachmentService.setUser(getUser());
GridFSDBFile file = organizationFileAttachmentService.getGridFSDBFileById(new ObjectId(id), "File");
if (file != null) {
try {
response.setContentType(file.getContentType());
response.setContentLength((new Long(file.getLength()).intValue()));
response.setHeader("content-Disposition", "attachment; filename=" + file.getFilename());// "attachment;filename=test.xls"
// copy it to response's OutputStream
IOUtils.copyLarge(file.getInputStream(), response.getOutputStream());
} catch (IOException ex) {
_logger.info("Error writing file to output stream. Filename was '" + id + "'");
throw new RuntimeException("IOError writing file to output stream");
}
}
}
Now it is working fine for me.
I'm using Phonegap with javascript and jquery. I've created a java servlet, it returns a pdf file. I can get de file correctly in a browser but I can't on Phonegap.
My code is this (javascript):
$.ajax({
type: "GET",
url: "http://x.x.x.x:xxxx/MyApp/PDF",
success: function(data, textStatus, request) {
alert("pdf OK");
window.open(data, "_system");
},
error: function(data, textStatus, request) {
alert("pdr error");
}
And here the servlet (this works fine from browser):
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
try {
// Create PDF (this works fine)
String ruta = getServletContext().getRealPath(reportTemplateUrl);
InputStream resourceAsStream = new FileInputStream(ruta);
jasperDesign = JRXmlLoader.load(resourceAsStream);
jasperReport = JasperCompileManager.compileReport(jasperDesign);
jasperPrint = JasperFillManager.fillReport(jasperReport, null, new JRBeanCollectionDataSource(findReportData(name)));
File pdf = new File("output.pdf");
JasperExportManager.exportReportToPdfStream(jasperPrint, new FileOutputStream(pdf));
// Send PDF
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename=output.pdf");
response.setContentLength((int) pdf.length());
InputStream fileInputStream = new FileInputStream(pdf);
OutputStream responseOutputStream = response.getOutputStream();
int bytes;
while ((bytes = fileInputStream.read()) != -1) {
responseOutputStream.write(bytes);
}
System.out.println("CREATED!");
} catch (JRException e) {
e.printStackTrace();
}
This code is running on iPad with Phonegap and always I get the OK alert. From the iPad's browser I can donwload and read the pdf (I put the URL in the browser as a normal page) and all is OK.
I think the problem is the "data", from javascript, I don't know if I need to save the file first or how to do to show it...
And yes, I need use a servlet and ajax, the PDF is dynamic. I don't mind open it with internal or external browser, but I need to see it.
Thanks!
:)
Well, I solved this doing a GET call on javascript opening the url on a new browser:
window.open("http://.../PDF?id=id&name=name&...", "_blank");
Without ajax.
Thanks everyone
I have a web application with a simple upload function. The idea is to allow user select a file and upon successfully upload, redirect to index.jsp.
However, although the file got uploaded, the response.redirect is not working. After a successfully upload, the page doesn't get redirected. It just stays there. The weird thing is that I can see it is processing the index.jsp from the tomcat server log even though it doesn;t get redirected.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//processRequest(request, response);
boolean status=false;
if (!ServletFileUpload.isMultipartContent(request)) {
throw new IllegalArgumentException("Request is not multipart, please 'multipart/form-data' enctype for your form.");
}
ServletFileUpload uploadHandler = new ServletFileUpload(new DiskFileItemFactory());
PrintWriter writer = response.getWriter();
response.setContentType("text/plain");
try {
List<FileItem> items = uploadHandler.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField()) {
File file = new File(getServletContext().getRealPath("/WEB-INF/upload"), item.getName());
item.write(file);
writer.write("{\"name\":\"" + item.getName() + "\",\"type\":\"" + item.getContentType() + "\",\"size\":\"" + item.getSize() + "\"}");
}
}
//redirect to index.jsp if successfully
redirect(request, response);
} catch (FileUploadException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
writer.close();
}
}
The redirect method:
private void redirect(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
The file upload plugin is from https://aquantum-demo.appspot.com/file-upload
I used the front-end and developed the upload event handler using java apache fileupload. Everything works fine except the redirect part.
The application.js file which handles the JSON returns:
$(function () {
// Initialize jQuery File Upload (Extended User Interface Version):
$('#file_upload').fileUploadUIX();
// Load existing files:
$.getJSON($('#file_upload').fileUploadUIX('option', 'url'), function (files) {
var options = $('#file_upload').fileUploadUIX('option');
options.adjustMaxNumberOfFiles(-files.length);
$.each(files, function (index, file) {
options.buildDownloadRow(file, options)
.appendTo(options.downloadTable).fadeIn();
});
});
});
Any ideas?
You're attempting to send two responses on a single request. One with JSON data in the response body and one which redirects the response to another request. This is not going to work. You can send only one response back per request. A redirect requires an untouched (uncommitted) response body, otherwise the redirect will just fail with IllegalStateException: response already committed in the server logs.
You need to move the redirect call from the servlet code to JavaScript code. Get rid of the redirect() line in the servlet and add the following line as the last line of the $.getJSON() callback function.
window.location = '/index.jsp';
This way JavaScript will take care of the redirect.