Displaying PDF from base64 string with Spring/Java - java

I have a PDF saved as a base 64 CLOB in a database.
As a functional test, I'm just trying to get it to display in my browser. I made a new endpoint in my controller and just put the base64 String into the controller, without even getting the PDF from the database, that looks like this:
#RequestMapping(value = "/output.pdf", method = RequestMethod.GET, produces = "application/pdf")
public void makePDF(HttpServletResponse response) throws Exception {
String value = "R04jArrrw45jNH6bV02="; //<--This is longer, but I shortened it for this question
byte[] imageByte = value.getBytes();
response.setContentType("application/pdf");
response.setContentLength(imageBytes.length);
response.getOutputStream().write(imageBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
Whenever I hit the endpoint, I get a Failed to load PDF document message. I can't figure out why.
I'm pretty new to this, so I'm having trouble figuring out what my next steps are. How do I get the PDF to display in the web browser?
EDIT
I was able to get this working, by using modifying my method to the following:
#RequestMapping(value = "/output.pdf", method = RequestMethod.GET, produces = "application/pdf")
public void makePDF(HttpServletResponse response) throws Exception {
try {
String value = "R04jArrrw45jNH6bV02="; //<--This is longer, but I shortened it for this question
byte[] image = Base64.decodeBase64(value.getBytes());
Document document = new Document();
document.setPageSize(PageSize.LETTER);
PdfWriter.getInstance(document, response.getOutputStream());
Image labelImage = Image.getInstance(image);
labelImage.setAlignment(Image.TOP);
labelImage.scalePercent(new Float("35"));
document.open();
document.add(labelImage);
response.setContentType("application/pdf");
response.setContentLength(imageBytes.length);
response.getOutputStream().write(image);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Trying to understand exactly what I'm doing here, and why it worked. Obviously has something to do with Base64 decoding, and using the Document object.

Stack Overflow post Blob vs Clob and why you should not store binary data in Clobs
PDF has a general text-based structure. However, PDF files can contain non-ASCII ("binary") data and should always be considered binary files, - sorry unable to find a source of truth link for this.
There is potential for a lossy encoding of data, and decoding encoded Base-64 in that case.
Decode using Base64 before you stream it out
Base64.decode(base64String, Base64.NO_WRAP)

Related

Rest response returned a pdf with random characters

#RequestMapping(value = "/merge", method = RequestMethod.POST, produces = "application/pdf")
public ResponseEntity<?> getPDF(#RequestBody DocRequest req) {
try {
ArrayList<String> urls = pdf.getFileList(req);
pdf.mergePdf(urls, req); // <-- this will generate a pdf called "untitled.pdf" on the server (outputstream)
byte[] pdf = Files.readAllBytes(new File("untitled.pdf").toPath());
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(pdf.length);
headers.set("Content-Disposition", "attachment; filename=untitled.pdf");
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
return new ResponseEntity<>(pdf ,headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
}
//if above did not return a proper response, then request is bad.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
If I open the untitled.pdf on the server it would look fine. But when I use swagger to download the pdf, I can see that the PDF contains random characters on the file.
The content image were okay, but the text in the pdf seems like bunch of question marks and some random characters such as example below
"7SLHZLYLHKILMVYL`V\LSLJ[`V\YPU]LZ[TLU[VW[PVUZ!
(YH[PVYLX\PYLTLU[VM!T\Z[ILTHPU[HPULK\ZPUNVULPU]LZ[TLU[VW[PVUMYVT,8(;VY(?(7YLTPLY=07MVYL]LY`VUL
V\[ZPKLT\[\HSM\UKZLSLJ[LKUV[PUJS\KPUN[OL.06VY:[HISL=HS\L-\UK
;OLTH_PT\TU\TI"
Why or what is causing this??
Update
The error when I tried to open the pdf through reader pops up the following message:
Cannot extract the embedded font 'CMOHAC+Helvetica-Light'. Some characters may not display or print correctly.
Also when I open up the pdf through reader I see bunch of dots instead of those characters if I just clicked on the pdf to open.
Try setting the Content-type header to application/pdf

How to properly open a png file

I am trying to attach a png file. Currently when I sent the email, the attachment is 2x bigger than the file should be and an invalid png file. Here is the code I currently have:
import com.sendgrid.*;
Attachments attachments = new Attachments();
String filePath = "/Users/david/Desktop/screenshot5.png";
String data = "";
try {
data = new String(Files.readAllBytes(Paths.get(filePath)));
} catch (IOException e) {
}
byte[] encoded = Base64.encodeBase64(data.getBytes());
String encodedString = new String(encoded);
attachments.setContent(encodedString);
Perhaps I am encoding the data incorrectly? What would be the correct way to 'get' the data to attach it?
With respect, this is why Python presents a problem to modern developers. It abstracts away important concepts that you can't fully understand in interpreted languages.
First, and this is a relatively basic concept, but you can't convert arbitrary byte sequences to a string and hope it works out. The following line is your first problem:
data = new String(Files.readAllBytes(Paths.get(filePath)));
EDIT: It looks like the library you are using expects the file to be base64 encoded. I have no idea why. Try changing your code to this:
Attachments attachments = new Attachments();
String filePath = "/Users/david/Desktop/screenshot5.png";
try {
byte[] encoded = Base64.encodeBase64(Files.readAllBytes(Paths.get(filePath)));
String encodedString = new String(encoded);
attachments.setContent(encodedString);
} catch (IOException e) {
}
The only issue you were having is that you were trying to represent arbitrary bytes as a string.
Take a look at the Builder class in the repository here. Example:
FileInputStream fileContent = new FileInputStream(filePath);
Attachments.Builder builder = new Attachments.Builder(fileName, fileContent);
mail.addAttachments(builder.build());

Convert base64 string to image at server side in java

I have base64 String which I want to convert back to image irrespective of image format at server side. I tried it by using following code, image is getting created but when I am trying to preview it, showing error could not load image.
public void convertStringToImage(String base64) {
try {
byte[] imageByteArray = decodeImage(base64);
FileOutputStream imageOutFile = new FileOutputStream("./src/main/resources/demo.jpg");
imageOutFile.write(imageByteArray);
imageOutFile.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "ImageStoreManager::convertStringToImage()" + e);
}
}
public static byte[] decodeImage(String imageDataString) {
return Base64.decodeBase64(imageDataString);
}
what should I do so that my image will look properly?
Your code looks fine. I can suggest however some more debugging steps for you.
Encode your file manually using, for example, this webpage
Compare if String base64 contains exact same content like you've got seen on the page. // if something wrong here, your request is corrupted, maybe some encoding issues on the frontend side?
See file content created under ./src/main/resources/demo.jpg and compare content (size, binary comparison) // if something wrong here you will know that actually save operation is broken
Remarks:
Did you try to do .flush() before close?
Your code in current form might cause resource leakage, have a look at try-with-resources
Try this:
public static byte[] decodeImage(String imageDataString) {
return org.apache.commons.codec.binary.Base64.decodeBase64(imageDataString.getBytes());
}

Spring mvc file upload

I'm working on spring mvc file upload. in the view, i use plupload multiple file upload plugin. here is my upload action:
#RequestMapping(value = CrudURI.uploadDo, method = RequestMethod.POST, produces = "application/json")
public #ResponseBody String uploadDo(#RequestBody MultipartFile file,
#RequestParam String name,
#RequestParam(required=false, defaultValue="-1") int chunks,
#RequestParam(required=false, defaultValue="-1") int chunk) {
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File("/uploads/"+name)));
stream.write(bytes);
stream.close();
} catch (Exception e) {
}
} else {
}
System.err.println(file.getOriginalFilename());
return null;
}
this action is working without any error or exception, but the uploaded file not saved. what's the problem?
thanks
Simply try following code:
File fileToSave=new File("/uploads/"+name);
file.transferTo(fileToSave);
And also make sure that you are actually getting file in mapping.
Darshan solution is correct, your original code has an issue that you're reading a file as getBytes() which is not using a buffer, but your writing using a buffer.
I think that your issue is most likely that you're trying to overwrite the uploaded file, at least it appears so based on your output location "/uploads/"+name. Try changing the name, and instead of having an empty catch block, add log for your exception it will point you to the issue

Convert byte[] to Base64 string for data URI

I know this has probably been asked 10000 times, however, I can't seem to find a straight answer to the question.
I have a LOB stored in my db that represents an image; I am getting that image from the DB and I would like to show it on a web page via the HTML IMG tag. This isn't my preferred solution, but it's a stop-gap implementation until I can find a better solution.
I'm trying to convert the byte[] to Base64 using the Apache Commons Codec in the following way:
String base64String = Base64.encodeBase64String({my byte[]});
Then, I am trying to show my image on my page like this:
<img src="data:image/jpg;base64,{base64String from above}"/>
It's displaying the browser's default "I cannot find this image", image.
Does anyone have any ideas?
Thanks.
I used this and it worked fine (contrary to the accepted answer, which uses a format not recommended for this scenario):
StringBuilder sb = new StringBuilder();
sb.append("data:image/png;base64,");
sb.append(StringUtils.newStringUtf8(Base64.encodeBase64(imageByteArray, false)));
contourChart = sb.toString();
According to the official documentation Base64.encodeBase64URLSafeString(byte[] binaryData) should be what you're looking for.
Also mime type for JPG is image/jpeg.
That's the correct syntax. It might be that your web browser does not support the data URI scheme. See Which browsers support data URIs and since which version?
Also, the JPEG MIME type is image/jpeg.
You may also want to consider streaming the images out to the browser rather than encoding them on the page itself.
Here's an example of streaming an image contained in a file out to the browser via a servlet, which could easily be adopted to stream the contents of your BLOB, rather than a file:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
ServletOutputStream sos = resp.getOutputStream();
try {
final String someImageName = req.getParameter(someKey);
// encode the image path and write the resulting path to the response
File imgFile = new File(someImageName);
writeResponse(resp, sos, imgFile);
}
catch (URISyntaxException e) {
throw new ServletException(e);
}
finally {
sos.close();
}
}
private void writeResponse(HttpServletResponse resp, OutputStream out, File file)
throws URISyntaxException, FileNotFoundException, IOException
{
// Get the MIME type of the file
String mimeType = getServletContext().getMimeType(file.getAbsolutePath());
if (mimeType == null) {
log.warn("Could not get MIME type of file: " + file.getAbsolutePath());
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
resp.setContentType(mimeType);
resp.setContentLength((int)file.length());
writeToFile(out, file);
}
private void writeToFile(OutputStream out, File file)
throws FileNotFoundException, IOException
{
final int BUF_SIZE = 8192;
// write the contents of the file to the output stream
FileInputStream in = new FileInputStream(file);
try {
byte[] buf = new byte[BUF_SIZE];
for (int count = 0; (count = in.read(buf)) >= 0;) {
out.write(buf, 0, count);
}
}
finally {
in.close();
}
}
If you don't want to stream from a servlet, then save the file to a directory in the webroot and then create the src pointing to that location. That way the web server does the work of serving the file. If you are feeling particularly clever, you can check for an existing file by timestamp/inode/crc32 and only write it out if it has changed in the DB which can give you a performance boost. This file method also will automatically support ETag and if-modified-since headers so that the browser can cache the file properly.

Categories