Output GIF from ServletOutputStream - java

I'm writing an endpoint that dynamically generates a GIF file. I'll go from the ground up.
I have a class named Function that works like an abstract class and I have several classes, in this example AddFunction, that represent small chunks of functionality. In this case, the AddFunction adds some numbers together. When the end point is hit, the ID of the AddFunction is passed to it (it could be any, in this example it's the add function). The code in the controller is as follows:
/**
* Returns the image for a function
*/
#RequestMapping(value = "/function/{functionId}/image.gif", produces = "image/gif")
public void getImage(#PathVariable(value = "functionId") String functionId, HttpServletResponse response) throws IOException {
Function function = functionService.getFunction(Integer.valueOf(functionId));
Logger logger = Logger.getLogger(FunctionController.class);
ServletOutputStream servOut = response.getOutputStream();
// Uses default values if you pass in nulls.
function.getImage(servOut, null, null);
servOut.flush();
servOut.close();
}
First, the Function is found by it's ID. I have checked, and the correct function is being found. This code is in need of some validation (for example checking the id passed in is a valid number) but I'll get to that later. I then grab the servlet output stream and pass it to the getImage methods of the function. This method generates the GIF that describes the function. This code looks like this:
public void getImage(OutputStream out, String staticContent, String changedContent) throws IOException {
String[] data = {"2", "+", "2", "=", "4"};
Logger logger = Logger.getLogger(AddFunction.class);
logger.info("Getting the add image.");
ImageUtils.writeSequenceToImage(ImageIO.createImageOutputStream(out), data, 5, Constants.IMAGE_HEIGHT / 2);
}
As you can see, it ignores the values and it is using stock data at the moment. It creates an array of values. Each of these values which appear in each frame of the GIF. So what I do is I take the ServletOutputStream and I use the ImageIO.createImageOutputStream to wrap that with an ImageOutputStream object. This is when passed into the writeSequenceToImage method in my own ImageUtils class. The last two values are coordinates for where to write from. In this case, the vertical middle of the image, on the far left. The code for the writeSequenceToImage method is as follows:
public static void writeSequenceToImage(ImageOutputStream out, String[] contentList, int x, int y) throws IOException {
StringBuilder dataBuilder = new StringBuilder();
Test test = new Test(out, BufferedImage.TYPE_INT_RGB, 500, true);
Logger logger = Logger.getLogger(ImageUtils.class);
logger.info("Writing sequence to image.");
for (String content : contentList) {
dataBuilder.append(content);
logger.info("writing " + dataBuilder.toString() + " to the gif.");
test.writeToSequence(generateAndWriteToImage(dataBuilder.toString(), x, y));
}
}
In this code, I am using the class Test (temporary name) which contains code that writes data to a GIF file. All I'm doing here is looping through and adding each value to a frame in the GIF. The code for class Test can be found here. What I do is I build up the String, so in our example the logs would output:
2014-12-31 14:37:15 INFO ImageUtils:48 - Writing sequence to image.
2014-12-31 14:37:15 INFO ImageUtils:53 - writing 2 to the gif.
2014-12-31 14:37:15 INFO ImageUtils:53 - writing 2+ to the gif.
2014-12-31 14:37:15 INFO ImageUtils:53 - writing 2+2 to the gif.
2014-12-31 14:37:15 INFO ImageUtils:53 - writing 2+2= to the gif.
2014-12-31 14:37:15 INFO ImageUtils:53 - writing 2+2=4 to the gif.
This will give the appearance in each frame of the GIF of it building up the string. Now, I write this to the GIF and I expect it to be pushed straight into the ServletOutputStream, only when I attempt to reference it with the following HTML:
<div class="panel columns large-12" ng-show="selectedFunction">
<h2>{{selectedFunction.name}}</h2>
<p>{{selectedFunction.description}}</p>
<p>This function expects a {{selectedFunction.expectedParameterType}} as a parameter.</p>
<p>This function will return a {{selectedFunction.expectedReturnType}}</p>
<img src="/autoalgorithm/functions/function/{{selectedFunction.id}}/image.gif" alt="{{selectedFunction.name}}"/>
</div>
I am seeing the following data returning in Chrome:
And I am seeing no image on my page:
What I have tried
I've tried to see the size of what is coming back. To do this, I have replaced the ServletOutputStream with a ByteArrayOutputStream so I can get the size of the data. If I do this, my code looks like this:
/**
* Returns the image for a function
*/
#RequestMapping(value = "/function/{functionId}/image.gif", produces = "image/gif")
public #ResponseBody byte[] getImage(#PathVariable(value = "functionId") String functionId, HttpServletResponse response) throws IOException {
Function function = functionService.getFunction(Integer.valueOf(functionId));
Logger logger = Logger.getLogger(FunctionController.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Uses default values if you pass in nulls.
function.getImage(baos, null, null);
logger.info("The number of bytes returned is " + baos.toByteArray().length);
return baos.toByteArray();
}
And the log outputs:
2014-12-31 15:34:09 INFO FunctionController:85 - The number of bytes returned is 0
So that is telling me that it isn't being written too. So I changed up my approach and refactored the code so I maintained a reference to the ImageOutputStream in my controller. This meant that I had complete control over the object, so now the log outputted:
2014-12-31 15:39:56 INFO FunctionController:85 - The number of bytes returned is 2708
Which was encouraging! And 2KB sounds about right for a very very simple GIF. However, when I check the response from Google, similar story:
Although this time it has a content length, but there is no preview available and the image still is not appearing.
I was wondering if anyone on here had tackled a similar issue? I suspect it is to do with the encoding of the GIF, but ImageIO doesn't support conversion from one stream to another, only from one BufferedImage type to another. So I used the ImageIO.read method to read it into a BufferedImage and used ImageIO.write to write it as a gif onto the ServletOutputStream. This yielded the following error:
java.lang.IllegalArgumentException: image == null!
At this point, I'm stumped. I'm hoping a fresh set of eyes can help me out. Does anyone have any ideas?

As already noted in the comments your question is a little bit unconcise but I'll try to show you how it can work.
First try the following:
#RequestMapping(value = "/function/{functionId}/image.gif", produces = "image/gif")
public void getImage(#PathVariable(value = "functionId") String functionId, HttpServletResponse response) throws IOException {
BufferedImage firstImage = ImageIO.read(new File("/bla.jpg"));
response.setContentType("image/gif"); // this should happen automatically
ImageIO.write(firstImage, "gif", response.getOutputStream());
response.getOutputStream().close();
}
Place some file named bla.jpg in your root directory or change the path to some existing image file (can also be a GIF). Make sure you have at least read access rights.
This should work in any case, regardless if jpg or gif file. If this doesn't work there may be something wrong with your Spring configuration. You should rule that out.
If this is working, you can use your method generateAndWriteToImage() to replace ImageIO.read(new File("/bla.jpg"));. And you should be done.
Now, I don't know what your generateAndWriteToImage() method does but I assume that it creates an instance of BufferedImage, writes some text into the image and returns it.
Something like this:
public static BufferedImage generateAndWriteToImage(String string, int x, int y) {
BufferedImage image = new BufferedImage(x,y,BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setPaintMode();
g.setFont(g.getFont().deriveFont(30f));
g.drawString(string, 100, 100);
g.dispose();
return image;
}
If you create the image with the type BufferedImage.TYPE_INT_RGB this shouldn't cause any problems.
TL;DR Another thing you already found out yourself is that a little refactoring gives you the ability to close the ImageOutputStream.
Assume the following method:
#RequestMapping(value = "/function/{functionId}/image.gif", produces = "image/gif")
public void getImage(#PathVariable(value = "functionId") String functionId, HttpServletResponse response) throws IOException {
Function function = functionService.getFunction(Integer.valueOf(functionId));
ServletOutputStream servOut = response.getOutputStream();
// Uses default values if you pass in nulls.
function.getImage(servOut, null, null);
servOut.flush();
servOut.close();
}
and this method:
public void getImage(OutputStream out, String staticContent, String changedContent) throws IOException {
String[] data = {"2", "+", "2", "=", "4"};
Logger logger = Logger.getLogger(AddFunction.class);
logger.info("Getting the add image.");
ImageUtils.writeSequenceToImage(ImageIO.createImageOutputStream(out), data, 5, Constants.IMAGE_HEIGHT / 2);
}
In the second method, you are creating a local instance of ImageOutputStream with ImageIO.createImageOutputStream(out) (last line).
I guess the main problem is, that you aren't closing this ImageOutputStream, this may result in data not beeing written to any other OutputStream (because of buffering).
To make it work you can refactor your methods to this:
#RequestMapping(value = "/function/{functionId}/image.gif", produces = "image/gif")
public void getImage(#PathVariable(value = "functionId") String functionId, HttpServletResponse response) throws IOException {
Function function = functionService.getFunction(Integer.valueOf(functionId));
ImageOutputStream servOut = ImageIO.createImageOutputStream(response.getOutputStream());
// Uses default values if you pass in nulls.
function.getImage(servOut, null, null);
servOut.close();
}
and this:
public void getImage(ImageOutputStream out, String staticContent, String changedContent) throws IOException {
String[] data = {"2", "+", "2", "=", "4"};
Logger logger = Logger.getLogger(AddFunction.class);
logger.info("Getting the add image.");
ImageUtils.writeSequenceToImage(out, data, 5, Constants.IMAGE_HEIGHT / 2);
}
The same thing applies here for the generateAndWriteToImage() method. If it correctly returns an instance of BufferedImage, this should work (with the refactoring).

I did not try with spring but tried with J2EE instead. Below approach works for me !
private void process(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
String baseServletPath = httpServletRequest.getServletContext().getRealPath("/");
System.out.println("Base Servlet Path :"+baseServletPath);
String relativeInputFilePath = "images/Tulips.gif";
String imageFilePath = baseServletPath + relativeInputFilePath;
File file = new File(imageFilePath);
BufferedImage bufferedImage = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
bufferedImage = ImageIO.read(file);
ImageIO.write(bufferedImage, "GIF", byteArrayOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
byte[] imageData = byteArrayOutputStream.toByteArray();
String relativeOutFilePath = "images/TulipsOut.gif";
String imageOutFilePath = baseServletPath + relativeOutFilePath;
File fileOut = new File(imageOutFilePath);
FileOutputStream fileOutputStream=null;
try {
fileOutputStream = new FileOutputStream(fileOut);
fileOutputStream.write(imageData);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
httpServletResponse.getOutputStream().write( imageData );
} catch (IOException e) {
e.printStackTrace();
}
}

Related

How to convert an image (rgb/gray) inside a pdf to monochrom/bitonal one using itext and java

I'm writing a java programm to swap images inside a pdf. Due to the process of generation they are stored as high dpi, rgb images, but are bitonal/monochrome images. I'm using itext 7.1.1, but also testet the latest dev version (7.1.2 snapshot).
I'm already able to extract the images from pdf and convert them to png or tif using indexed colours or gray (0 & 255 only) in imagemagick (also testet gimp).
I modified some code from itext, to replace the images inside the pdf, which does work for DeviceRGB- and DeviceGray-Images, but not for Bitonal ones:
public static Image readPng(String pImageFolder, int pImageNumber) throws IOException {
String url = "./" + pImageFolder + "/" + pImageNumber + ".png";
File ifile = new File(url);
if (ifile.exists() && ifile.isFile()) {
return new Image(ImageDataFactory.create(url));
} else {
return null;
}
}
public static void replaceStream(PdfStream orig, PdfStream stream) throws IOException {
orig.clear();
orig.setData(stream.getBytes());
for (PdfName name : stream.keySet()) {
orig.put(name, stream.get(name));
}
}
public static void replaceImages(String pFilename, String pImagefolder, String pOutputFilename) throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(pFilename), new PdfWriter(pOutputFilename));
for (int i = 0; i < pdfDoc.getNumberOfPages(); i++) {
PdfDictionary page = pdfDoc.getPage(i + 1).getPdfObject();
PdfDictionary resources = page.getAsDictionary(PdfName.Resources);
PdfDictionary xobjects = resources.getAsDictionary(PdfName.XObject);
Iterator<PdfName> iter = xobjects.keySet().iterator();
PdfName imgRef;
PdfStream stream;
Image img;
int number;
while (iter.hasNext()) {
imgRef = iter.next();
number = xobjects.get(imgRef).getIndirectReference().getObjNumber();
stream = xobjects.getAsStream(imgRef);
img = readPng(pImagefolder, number);
if (img != null) {
replaceStream(stream, img.getXObject().getPdfObject());
}
}
}
pdfDoc.close();
}
If i convert the images to tif and use them as replacement, there are dark images (all pixels are black) inside the pdf. If i try to use png-images, they are not shown and pdfimages complaints "Unknown compression method in flate stream".
FYI:
There was an error in my replaceStream: getBytes() deflates a PdfStream. All Stream-Attributes were copied, thus there was a Filter-Information saying FlateDecoding is necessary.
I had to tell getBytes()not to deflate by setting the decoded-Parameter to false: getBytes(false)
public static void replaceStream(PdfStream orig, PdfStream stream) throws IOException {
orig.clear();
orig.setData(stream.getBytes(false));
for (PdfName name : stream.keySet()) {
orig.put(name, stream.get(name));
}
}
Now everything works fine, except:
Bitone-images are not CCITT4, which they should be. (Doesn't matter, because they are converted to JBig2.)
Images are said to have an error by Acrobat, but every other viewer displays just fine: There seems to be an error inside the ColorSpace information. That should be DeviceGray, but is CalGray with some Gamma-Information, but missing WhitePoint. Changing to DeviceGray by hand makes it work. A workaround is to strip gAMA and cHRM.
Both are conversion errors in iText7:
CCITT4: PNGImageHelper line 254 should be RawImageHelper.updateRawImageParameters(png.image, png.width, png.height, components, bpc, png.idat.toByteArray(), null); to trigger conversion.
WhitePoint is correctly read from the file and stored inside the ImageData-Class, but is discarded inside PdfImageXObject -> createPdfStream.

Serializing an object that includes BufferedImages

As the title suggests, I'm trying to save to file an object that contains (among other variables, Strings, etc) a few BufferedImages.
I found this:
How to serialize an object that includes BufferedImages
And it works like a charm, but with a small setback: it works well if your object contains only ONE image.
I've been struggling to get his solution to work with more than one image (which in theory should work) but each time I read the file in, I get my object back, I get the correct number of images, but only the first image actually gets read in; the others are just null images that have no data in them.
This is how my object looks like:
class Obj implements Serializable
{
transient List<BufferedImage> imageSelection= new ArrayList<BufferedImage>();
// ... other vars and functions
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(imageSelection.size()); // how many images are serialized?
for (BufferedImage eachImage : imageSelection) {
ImageIO.write(eachImage, "jpg", out); // png is lossless
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final int imageCount = in.readInt();
imageSelection = new ArrayList<BufferedImage>(imageCount);
for (int i=0; i<imageCount; i++) {
imageSelection.add(ImageIO.read(in));
}
}
}
This is how I'm writing and reading the object to and from a file:
// writing
try (
FileOutputStream file = new FileOutputStream(objName+".ser");
ObjectOutputStream output = new ObjectOutputStream(file);
){
output.writeObject(myObjs);
}
catch(IOException ex){
ex.printStackTrace();
}
// reading
try(
FileInputStream inputStr = new FileInputStream(file.getAbsolutePath());
ObjectInputStream input = new ObjectInputStream (inputStr);
)
{myObjs = (List<Obj>)input.readObject();}
catch(Exception ex)
{ex.printStackTrace();}
Even though I have a list of objects, they get read in correctly and each element of the list is populated accordingly, except for the BufferedImages.
Does anyone have any means of fixing this?
The problem is likely that ImageIO.read(...) incorrectly positions the stream after the first image read.
I see two options to fix this:
Rewrite the serialization of the BufferedImages to write the backing array(s) of the image, height, width, color model/color space identifer, and other data required to recreate the BufferedImage. This requires a bit of code to correctly handle all kinds of images, so I'll skip the details for now. Might be faster and more accurate (but might send more data).
Continue to serialize using ImageIO, but buffer each write using a ByteArrayOutputStream, and prepend each image with its byte count. When reading back, start by reading the byte count, and make sure you fully read each image. This is trivial to implement, but some images might get converted or lose details (ie. JPEG compression), due to file format constraints. Something like:
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(imageSelection.size()); // how many images are serialized?
for (BufferedImage eachImage : imageSelection) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ImageIO.write(eachImage, "jpg", buffer);
out.writeInt(buffer.size()); // Prepend image with byte count
buffer.writeTo(out); // Write image
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
int imageCount = in.readInt();
imageSelection = new ArrayList<BufferedImage>(imageCount);
for (int i = 0; i < imageCount; i++) {
int size = in.readInt(); // Read byte count
byte[] buffer = new byte[size];
in.readFully(buffer); // Make sure you read all bytes of the image
imageSelection.add(ImageIO.read(new ByteArrayInputStream(buffer)));
}
}

How do I Execute Java from Java?

I have this DownloadFile.java and downloads the file as it should:
import java.io.*;
import java.net.URL;
public class DownloadFile {
public static void main(String[] args) throws IOException {
String fileName = "setup.exe";
// The file that will be saved on your computer
URL link = new URL("http://onlinebackup.elgiganten.se/software/elgiganten/setup.exe");
// The file that you want to download
// Code to download
InputStream in = new BufferedInputStream(link.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
in.close();
byte[] response = out.toByteArray();
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(response);
fos.close();
// End download code
System.out.println("Finished");
}
}
I want to execute this from a mouse event in Gui.java.
private void jLabel17MouseClicked(java.awt.event.MouseEvent evt){
}
How do I do this?
Your current method is a static method, which is fine, but all the data that it extracts is held tightly within the main method, preventing other classes from using it, but fortunately this can be corrected.
My suggestion:
re-write your DownloadFile code so that it is does not simply a static main method, but rather a method that can be called by other classes easily, and that returns the data from the file of interest. This way outside classes can call the method and then receive the data that the method extracted.
Give it a String parameter that will allow the calling code to pass in the URL address.
Give it a File parameter for the file that it should write data to.
Consider having it return data (a byte array?), if this data will be needed by the calling program.
Or if it does not need to return data, perhaps it could return boolean to indicate if the download was successful or not.
Make sure that your method throws all exceptions (such as IO and URL excptions) that it needs to throw.
Also, if this is to be called by a Swing GUI, be sure to call this type of code in a background thread, such as in a SwingWorker, so that this code does not tie up the Swing event thread, rendering your GUI frozen for a time.

Error while retrieving images from pdf using Itext

I have an existing PDF from which I want to retrieve images
NOTE:
In the Documentation, this is the RESULT variable
public static final String RESULT = "results/part4/chapter15/Img%s.%s";
I am not getting why this image is needed?I just want to extract the images from my PDF file
So Now when I use MyImageRenderListener listener = new MyImageRenderListener(RESULT);
I am getting the error:
results\part4\chapter15\Img16.jpg (The system
cannot find the path specified)
This is the code that I am having.
package part4.chapter15;
import java.io.IOException;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
/**
* Extracts images from a PDF file.
*/
public class ExtractImages {
/** The new document to which we've added a border rectangle. */
public static final String RESOURCE = "resources/pdfs/samplefile.pdf";
public static final String RESULT = "results/part4/chapter15/Img%s.%s";
/**
* Parses a PDF and extracts all the images.
* #param src the source PDF
* #param dest the resulting PDF
*/
public void extractImages(String filename)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(filename);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
MyImageRenderListener listener = new MyImageRenderListener(RESULT);
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
parser.processContent(i, listener);
}
reader.close();
}
/**
* Main method.
* #param args no arguments needed
* #throws DocumentException
* #throws IOException
*/
public static void main(String[] args) throws IOException, DocumentException {
new ExtractImages().extractImages(RESOURCE);
}
}
You have two questions and the answer to the first question is the key to the answer of the second.
Question 1:
You refer to:
public static final String RESULT = "results/part4/chapter15/Img%s.%s";
And you ask: why is this image needed?
That question is wrong, because Img%s.%s is not a filename of an image, it's a pattern of the filename of an image. While parsing, iText will detect images in the PDF. These images are stored in numbered objects (e.g. object 16) and these images can be exported in different formats (e.g. jpg, png,...).
Suppose that an image is stored in object 16 and that this image is a jpg, then the pattern will resolve to Img16.jpg.
Question 2:
Why do I get an error:
results\part4\chapter15\Img16.jpg (The system cannot find the path specified)
In your PDF, there's a jpg stored in object 16. You are asking iText to store that image using this path: results\part4\chapter15\Img16.jpg (as explained in my answer to Question 1). However: you working directory doesn't have the subdirectories results\part4\chapter15\, hence an IOException (or a FileNotFoundException?) is thrown.
What is the general problem?
You have copy/pasted the ExtractImages example I wrote for my book "iText in Action - Second Edition", but:
You didn't read that book, so you have no idea what that code is supposed to do.
You aren't telling the readers on StackOverflow that this example depends on the MyImageRenderer class, which is where all the magic happens.
How can you solve your problem?
Option 1:
Change RESULT like this:
public static final String RESULT = "Img%s.%s";
Now the images will be stored in your working directory.
Option 2:
Adapt the MyImageRenderer class, more specifically this method:
public void renderImage(ImageRenderInfo renderInfo) {
try {
String filename;
FileOutputStream os;
PdfImageObject image = renderInfo.getImage();
if (image == null) return;
filename = String.format(path,
renderInfo.getRef().getNumber(), image.getFileType());
os = new FileOutputStream(filename);
os.write(image.getImageAsBytes());
os.flush();
os.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
iText calls this class whenever an image is encountered. It passed an ImageRenderInfo to this method that contains plenty of information about that image.
In this implementation, we store the image bytes as a file. This is how we create the path to that file:
String.format(path,
renderInfo.getRef().getNumber(), image.getFileType())
As you can see, the pattern stored in RESULT is used in such a way that the first occurrence of %s is replaced with a number and the second occurrence with a file extension.
You could easily adapt this method so that it stores the images as byte[] in a List if that is what you want.

Java Servlet PNG image output display corrupt (not transmitted completely)

I have a problem with some Java servlet I developed a few days ago and finally uploaded it to my test application server.
First I will describe what the Servlet does: Basically it creates a website counter by using two parameters: First parameter is the usernames identifier (username) and the second one is an integer identity which identifies the background image and design which should be applied to the counter, so that the user can change the used counter design without losing his counts. I use a Java servlet to generate the PNG file in these steps:
Load counter and design from database to obtain data
Load background image from PNG image source file to a BufferedImage
Draw the current counter value to the background image using the specified font, xPos, yPos, font size, etc..
Write the BufferedImage as PNG file to the servlet output stream specifying the content type is PNG.
The problem here is that the image which will be generated is incomplete (!). I do a flush and close on the output stream buy anyway it is still incomplete. I also tried to leave out all my modifications to draw anything on the Graphics2D of the BufferedImage, but it did not help at all. When I run the same code base in a stand alone application the output image is just fine so I don't think there is a problem with ImageIO or my general code.
Perhaps I missed some mandatory stuff in the servlet output declaration, but I can't find the bug on my own.
Here is the real running application:
http://csz-online.net:8080/CounterBuilder/CounterServlet?name=clemens&layout=61
As you can see the file in incomplete! Firefox for example shows the PNG content it received, but if I download the file Eye of Gnome for example won't display the corrupt file at all! The same image created by the stand alone application is complete and works fine.
THANKS!
Here is the code of the Java servlet processRequest method, which is to be executed on post and get. Since I don't have the original source code at the moment this is what JD-GUI outputs. It pretty looks the same except the exception handling at the end of the method:
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ServletContext cntx = getServletContext();
try
{
String creationStr = request.getParameter("layout");
long cId = Long.valueOf(creationStr).longValue();
String name = request.getParameter("name");
Counter counter = this.counterFacade.findByNickname(name);
Creation creation = this.creationFacade.find(Long.valueOf(cId));
String filename = cntx.getRealPath(creation.getBackground());
if (filename == null) {
throw new FileNotFoundException("BG not found: " + creation.getBackground());
}
String mime = cntx.getMimeType(filename);
int cCounter = counter.getCounter() + 1;
response.setContentType(mime);
File file = new File(filename);
response.setContentLength((int)file.length());
OutputStream out = response.getOutputStream();Throwable localThrowable4 = null;
try
{
String fontStr = creation.getFont();
BufferedImage image = ImageIO.read(file);
BufferedImage[] digits = new BufferedImage[10];
image.flush();
switch (fontStr)
{
case "gold":
Graphics2D g = image.createGraphics();
digits[0] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/0.png")));
digits[1] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/1.png")));
digits[2] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/2.png")));
digits[3] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/3.png")));
digits[4] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/4.png")));
digits[5] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/5.png")));
digits[6] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/6.png")));
digits[7] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/7.png")));
digits[8] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/8.png")));
digits[9] = ImageIO.read(new File(cntx.getRealPath("images/digits/gold/9.png")));
drawNumbersToImage(g, digits, cCounter, creation.getxPos(), creation.getyPos());
break;
default:
Font font = new Font(creation.getFont(), 0, creation.getFontSize());
FontRenderContext frc = new FontRenderContext(null, true, true);
TextLayout layout = new TextLayout(String.valueOf(cCounter), font, frc);
Graphics2D h = image.createGraphics();
h.setColor((Color)Color.class.getField(creation.getColor()).get(null));
layout.draw(h, creation.getxPos(), creation.getyPos());
h.dispose();
}
counter.setCounter(cCounter);
this.counterFacade.edit(counter);
ImageIO.write(image, "PNG", out);
out.flush();
out.close();
}
catch (Throwable localThrowable1)
{
localThrowable4 = localThrowable1;throw localThrowable1;
}
finally
{
if (out != null) {
if (localThrowable4 != null) {
try
{
out.close();
}
catch (Throwable x2)
{
localThrowable4.addSuppressed(x2);
}
} else {
out.close();
}
}
}
}
catch (IOException|NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException ex)
{
ex.printStackTrace();
response.setContentType("text/HTML");
PrintWriter writer = response.getWriter();Throwable localThrowable3 = null;
try
{
writer.print("Could not generate counter due to missing or invalid parameters <b>bg</b> and <b>name</b>.");
}
catch (Throwable localThrowable2)
{
localThrowable3 = localThrowable2;throw localThrowable2;
}
finally
{
if (writer != null) {
if (localThrowable3 != null) {
try
{
writer.close();
}
catch (Throwable x2)
{
localThrowable3.addSuppressed(x2);
}
} else {
writer.close();
}
}
}
}
}

Categories

Resources