I am trying to open a PDF file, saved in the server, using a Java Restful service and angularjs.
My code for the service in Java is:
#GET
#Path("/getPDF")
#Produces("application/pdf")
public Response getPDF() throws FileNotFoundException {
File file = new File("/path/to/file.pdf");
FileInputStream fileInputStream;
fileInputStream = new FileInputStream(file);
long contentLength = file.length();
ResponseBuilder responseBuilder = Response.ok((Object) fileInputStream);
responseBuilder.type("application/pdf");
responseBuilder.header("Content-Disposition", "inline; filename=file.pdf");
responseBuilder.header("Content-Length", contentLength);
responseBuilder.header("charset", "utf-8");
return responseBuilder.build();
}
Once the Response is returned I handle the request with angularjs:
MyJavaService.getPDF().success(function(data){
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
$window.open(fileURL);
});
The result is that a new window opens, with the same number of pages like the PDF file (correct), but the content is not displayed and all the pages are white (not correct).
Does anyone have any clue of what am I doing wrong?
I'm assuming you're using a $http.get() call.
You probably having something along the lines of:
$http.get(<URL>, {params: <stuff>})
You need to tell angular that the responseType is an arrayBuffer like so:
$http.get(<URL>, {responseType: 'arraybuffer', params: <stuff>})
Don't hesitate to tell me my assumptions are incorrect.
p.s.: try to avoid using .success() and instead use .then(). The former is deprecated 1.4+ and completely removed in 1.6+
Related
I am trying to send a PDF that will be created in memory from one API to another API which will then render it in an HTML page. Info about my technologies:
Springboot
Microservices Architecture (with Eureka)
Java
Thymeleaf
What I have so far is a microservice which receives a String input from an input field (through an API) and then I get it here and I believe I prepare a pdf in memory(haven't tested it yet):
public InputStream convert(String input) throws FileNotFoundException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(out);
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
document.add(new Paragraph(input));
document.close();
return new ByteArrayInputStream(out.toByteArray());
}
Here is my sending controller currently:
#RequestMapping("/cnv")
public InputStream doConversion(#RequestParam(defaultValue = "0") String input) {
try {
return textToPDFService.c2f(input);
} catch (FileNotFoundException e) {
throw new RuntimeException("Exception thrown while writing file: " + e);
}
}
I don't have any code to show for the web server that receives it yet, but you can expect since this is Springboot I am going to have a relevant endpoint in a #Controller and a method that communicates with my microservice of type #Service.
Question is, how do I receive this in InputStream in my web server's service and render it? Helpful resources are also welcome.
PS: I have never used iText before, Springboot or microservices prior to this. Also, never had a requirement about PDFs (yeah, I know I am a big noob).
It seems I got my answer. Make sure you return in your controller that performs the conversions a byte[] or even better ResponseEntity<byte[]>. Then, you should add headers to your request like so:
return ResponseEntity.ok()
.header(headerKey, headerValue)
.contentType(MediaType.APPLICATION_PDF)
.body(res);
On your receiving microservice you need a service which will do the following:
// Setup Headers & URL
String url = blah blah..;
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_PDF));
HttpEntity<String> entity = new HttpEntity<>("body", headers);
// Get Result from Microservice
return restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class, input);
Of course, this will return to you this ResponseEntity<byte[]>. You can just pass this to the controller endpoint and you are done:
return textToPDFService.textToPDFRequest(input);
Keep in mind you should mind exceptions and HTTP codes, but this will do as a minimal solution.
I've been trying to do file upload to a server from Javascript to a Jakarta REST service. The file is not getting decoded and I can't find what's wrong.
The upload code I'm using in javascript is basic FormData upload (vanillajs):
var url=.....
var xhttp = new XMLHttpRequest();
var formData = new FormData();
xhttp.onreadystatechange = () => {
if (this.readyState == 4)
{
if (this.status == 200)
{
if (successCallback != null) successCallback(this.responseText);
}
else
{
if (errorCallback != null) errorCallback(this.status, this.statusText);
}
}
};
xhttp.open("POST", url, true);
formData.append("file", file);
xhttp.send(formData);
The way I'm getting the file is to use a file input, and when the user loads an image, I load a picture of it and use that (React):
const [imgSrc, setImgSrc] = React.useState('');
const loadImage=(e)=>{
var file=e.target.files[0];
var reader = new FileReader();
reader.onload = (e2)=>{
setImgSrc(e2.target.result);
}
reader.readAsDataURL(file);
}
<input type="file" accept="image/*" onChange={(e)=>loadImage(e)} />
That seems to work fine. When I look at the request headers I see:
Content-Type: multipart/form-data; boundary=....
and the payload starts with:
file: data:image/jpeg;base64,/9j/...
Then the REST service I'm using to receive this is:
#POST
#Path("/submit")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadImage(
#FormDataParam("file") File fileBody,
#FormDataParam("file") FormDataContentDisposition metadata)
{
// set up S3 bucket, all that works fine
s3.putObject("mybucket", "pick_a_name", new FileInputStream(fileBody), null);
}
and that works, BUT, the file content is the literal content of the payload:
data:image/jpeg;base64,/9j/...
So it's not being recognized as a base 64 encoded file content and being decoded.
I've gone through various iterations and questions, including variations that result in 400 or 415 errors, but as far as I can tell these things look like they should line up.
A few more data points:
Tomcat 10
Jakarta 3.0.0
Jersey 3.0.3 (and jersey media multipart 3.0.3)
Java 8
Any suggestions would be appreciated. Thanks!
I'd like to get a "real" (what's the correct problem and solution) answer, but in the meantime I went with a hack:
InputStream outStream = hackBase64Decode(fileBody);
s3.putObject("mybucket", "pick_a_name", outStream, null);
private InputStream hackBase64Decode(File fileBody) throws Exception
{
FileInputStream fs = new FileInputStream(fileBody);
int ch;
do {
ch = fs.read();
} while (ch != ',');
return Base64.getDecoder().wrap(fs);
}
The best that can be said for that is it works. Again, though, would prefer a real answer.
I use spring RestTemplate to call the the Third-party services,
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
result like this:
forEntity
status:200
headers:
content-type=audio/wav
body:"RIFFä,,,xxxxxxx......"
response is enter image description here
the body seems to wav data, I want to save the data to wav file.
if I Go directly to the link in chrome, it's ok to play, and download.
Use RestTemplate.execute instead, which allows you to attach a ResponseExtractor, in which you have access to the response body which an InputStream, we take that InputStream and write it to a file
restTemplate.execute(
url,
HttpMethod.GET,
request -> {},
response -> {
//get response body as inputstream
InputStream in = response.getBody();
//write inputstream to a local file
Files.copy(in, Paths.get("C:/path/to/file.wav"), StandardCopyOption.REPLACE_EXISTING);
return null;
}
);
In Java a String is not the same as a byte[] array.
Therefore treating audio-content (which is binary data)
as a string (i.e. text data) is begging for problems.
Instead, you should get the response body as byte[].
Then you can save the bytes to a file.
For example like this:
ResponseEntity<byte[]> entity = restTemplate.getForEntity(url, byte[].class);
byte[] body = entity.getBody();
Path path = Paths.get("example.wav");
Files.write(path, body);
I am trying to download a zip file from a fixed location present in server.
In my Rest method , I am just passing the file name from client (browser) .
(Please see below code ).
In my Rest method I am sending the zip file to the client.
The file gets downloaded on the browser without any issue.
My Issue is that the zip file gets downloaded on browser without .zip extension.
#RequestMapping(value = "/zip/{filePath}", method = RequestMethod.GET)
public #ResponseBody void downloadZip(#PathVariable("filePath") String filePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletContext context = request.getServletContext();
File downloadFile = new File(filePath);
FileInputStream inputStream = new FileInputStream(downloadFile);
// get output stream of the response
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[(int) downloadFile.length()];
int bytesRead = -1;
// write bytes read from the input stream into the output stream
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
// get MIME type of the file
String mimeType = context.getMimeType(fullPath);
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
// set content attributes for the response
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
response.setHeader("Content-Disposition",
String.format("attachment; filename=\"%s\"", downloadFile.getName()));
logger.error("Filename = " + downloadFile.getName());
inputStream.close();
outStream.close();
}
PS: The file gets downloaded on some machine with ZIP and in some machine without ZIP. I have tested only on chrome (as per client requirement).
I think, there is an issue with the Chrome settings which I need to look upon (just a guess).
Can someone help upon this?
Thanks in advance....
Change the order between setting the response headers and shoving the file down the output stream - after all, the headers need to leave first.
[Edited]
"Why setting HttpServletResponse in starting effects the code."
Well, simple: the client is supposed to receive instructions of what to do with the payload by interpreting the HTTP response headers. If those are not set in the beginning, sending those headers at the end of the transmission comes too late. And this assumes the HttpServletResponse will actually send those headers when invoked with setHeader, which is a big assumption - I suspect those headers will not actually be sent after calling response.getOutputStream - it is unlikely the response will buffer the entire payload to wait for the caller to specify those headers.
I have PDFs mounted on an external server. I have to access them in my Java servlet and push them to the clients browser. The PDF should get downloaded directly or it may open a 'SAVE or OPEN' dialog window.
This is what i am trying in my code but it could not do much.
URL url = new URL("http://www01/manuals/zseries.pdf");
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream in = url.openStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = in.read(chunk)) != -1 ) {
bais.write(chunk, 0, n);
}
I have tried many ways to do this but could not succeed. I welcome if you have any good method to do this!
When you read the data, you get it inside your program memory, which is on the server side. To get it to the user's browser, you have to also write everything that you have read.
Before you start writing, though, you should give some appropriate headers.
Indicate that you are sending over a PDF file, by setting the mime type
Set the content length.
Indicate that the file is intended for download rather than showing inside the browser.
To set the mime type, use
response.setContentType("application/pdf");
To set the content length, assuming it's the same content length that you get from the URL, use:
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if ( connection.getResponseCode() == 200 ) {
int contentLength = connection.getContentLength();
response.setContentLength( contentLength );
To indicate that you want the file to be downloaded, use:
response.setHeader( "Content-Disposition", "attachment; filename=\"zseries.pdf\"";
(Take care to change the file name to whatever you want the user to see in the save dialog box)
Finally, get the input stream from the URLConnection you just opened, get the servlet's response output stream, and start reading from one and writing to the other:
InputStream pdfSource = connection.getInputStream();
OutputStream pdfTarget = response.getOutputStream();
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int n =0;
while ( (n = pdfSource.read(chunk)) != -1 ) {
pdfTarget.write(chunk, 0, n);
}
} // End of if
Remember to use try/catch around this, because most of these methods throw IOException, timeout exceptions etc., and to finally close both streams. Also remember to do something meaningful (like give an error output) in case the response was not 200.
You could transfer the byte array to the client, then use Itext to "stamp" the pdf in a new file. After that use java.awt.Desktop to lauch the file.
public static void lauchPdf(byte[] bytes, String fileName) throws DocumentException, IOException{
PdfReader reader = new PdfReader(bytes);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(fileName));
stamper.close();
Desktop dt = Desktop.getDesktop();
dt.browse(getFileURI(fileName));
}
You don't need to push anything (hope you really don't, because actually you can't). From the perspective of the browser making the request, you could get the PDF from the database, generate it on the fly or read it from the filesystem (which is your case). So, let's say you have this in your HTML:
DOWNLOAD FILE
you need to register a servlet for /dl/* and implement the doGet(req, resp) like this:
public void doGet(
HttpServletRequest req
, HttpServletResponse resp
) throws IOException {
resp.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + suggestFilename(req) + "\"");
// Then copy the stream, for example using IOUtils.copy ...
// lookup the URL from the bits after /dl/*
URL url = getURLFromRequest(req);
InputStream in = url.openConnection().getInputStream();
IOUtils.copy(in, resp.getOutputStream());
fin.close();
}
IOUtils is from Apache Commons IO (or just write your own while loop)