maxGraph/java/examples/com/mxgraph/examples/web/EmbedImage.java

369 lines
11 KiB
Java

package com.mxgraph.examples.web;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPOutputStream;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mxgraph.util.mxBase64;
import com.mxgraph.util.mxUtils;
/**
*
*/
public class EmbedImage extends HttpServlet
{
/**
*
*/
private static final long serialVersionUID = -4951624126588618796L;
/**
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
OutputStream out = response.getOutputStream();
String encoding = request.getHeader("Accept-Encoding");
// Supports GZIP content encoding
if (encoding != null && encoding.indexOf("gzip") >= 0)
{
response.setHeader("Content-Encoding", "gzip");
out = new GZIPOutputStream(out);
}
PrintWriter writer = new PrintWriter(out);
writer.println("<html>");
writer.println("<head>");
writer.println("</head>");
writer.println("<body>");
writer.println("<script type=\"text/javascript\">");
// Shows an error message on the client-side if the file is larger than
// 32 KB, which is the maximum size foe data URIs in IE8.
// LATER: Define correct size so that image can have max of 32K
if (request.getContentLength() < 40000)
{
Map<String, String> post = parseMultipartRequest(request);
byte[] img = post.get("upfile").getBytes(ENCODING);
String data = mxBase64.encodeToString(img, false);
String name = post.get("filename");
int index = name.lastIndexOf('\\');
if (index >= 0)
{
name = name.substring(index + 1);
}
name += "-" + new Date().getTime();
String url = "null";
if (post.get("dataurl").equals("false"))
{
url = "'mhtml:'+window.location.href+'?img=" + name + "!" + name + "'";
// In theory, up to 4K in 300 cookies can be used to transfer the data
// directly in the GET request of the image to avoid a session state.
// This can be done by splitting the base64 data into 4K cookies in the
// insert method in the client and then forcing an image prefetch with
// the respective MHTML URL for the image. On the server-side all we
// we need to do is fetch the content location (get param) and data.
// In practice most servers have a header size limit of 8-16K.
// LATER: Use cookieless sessions
request.getSession(true).setAttribute("image",
"<!--\n" + mhtml(name, data) + "-->");
}
writer.println("window.parent.insert(\"" + name
+ "\", \"data:image/png," + data + "\", " + url + ");");
}
else
{
writer.println("alert(\"File exceeds the maximum allowed size. File must be less than 32KB.\");");
}
// NOTE: Ignore "Failed to load resource" error in Chrome,
// see http://code.google.com/p/chromium/issues/detail?id=29180
writer.println("var iframe = window.parent.document.getElementById('embedimageframe');");
writer.println("iframe.parentNode.removeChild(iframe);");
writer.println("</script>");
writer.println("</body>");
writer.println("<head>");
writer.flush();
writer.close();
}
/**
*
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
OutputStream out = response.getOutputStream();
String encoding = request.getHeader("Accept-Encoding");
// Supports GZIP content encoding
if (encoding != null && encoding.indexOf("gzip") >= 0)
{
response.setHeader("Content-Encoding", "gzip");
out = new GZIPOutputStream(out);
}
PrintWriter writer = new PrintWriter(out);
String url = request.getParameter("url");
if (url != null)
{
// Enables caching of this response
response.setHeader("Content-Type", "text/plain");
response.setHeader("Cache-Control", "private");
response.setHeader("Expires", "Thu, 15 Apr 2030 20:00:00 GMT");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(ImageIO.read(new URL(url)), "png", bos);
writer.print("<!--\n"
+ mhtml("image",
mxBase64.encodeToString(bos.toByteArray(), false))
+ "-->\n");
}
else if (request.getSession(false) != null)
{
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control",
"no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
response.setHeader("Content-Type", "text/plain");
// Gets the image from the session and destroys the session and the cookie
writer.print(request.getSession().getAttribute("image"));
request.getSession().invalidate();
Cookie cookie = new Cookie("JSESSIONID", "");
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
else
{
// Makes sure there is no caching on the client side
response.setHeader("Cache-control", "private, no-cache, no-store");
response.setHeader("Expires", "0");
response.setStatus(HttpServletResponse.SC_OK);
// Loads the static HTML page with the placeholder from the template
String page = mxUtils.readFile(Roundtrip.class.getResource(
"/com/mxgraph/examples/web/resources/embedimage.html")
.getPath());
String userAgent = request.getHeader("User-Agent");
boolean dataUrl = userAgent.indexOf("MSIE 6") < 0
&& userAgent.indexOf("MSIE 7") < 0;
// In a real-world environment the following would be done for each entry
// of the image bundle in an XML file for a diagram.
String name = "myImage";
String data = "R0lGODlhEAAQAMIGAAAAAICAAICAgP//AOzp2O3r2////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAHACwAAAAAEAAQAAADTXi63AowynnAMDfjPUDlnAAJhmeBFxAEloliKltWmiYCQvfVr6lBPB1ggxN1hilaSSASFQpIV5HJBDyHpqK2ejVRm2AAgZCdmCGO9CIBADs";
String mhtml = (dataUrl) ? "" : "\n" + mhtml(name, data);
String bundle = "bundle.putImage('myImage', 'data:image/png," + data
+ "=', 'mhtml:' + window.location.href + '!" + name + "');";
// Replaces the placeholder in the template with the XML data
// which is then parsed into the graph model on the client.
// In a production environment you should use a template engine.
page = page.replaceAll("%mhtml%", mhtml);
page = page.replaceAll("%dataUrl%", (dataUrl) ? "true" : "false");
page = page.replaceAll("%bundle%", bundle);
writer.println(page);
}
writer.flush();
writer.close();
}
/**
* Creates a MHTML entry for the given resource name and base64 encoded data.
*/
protected String mhtml(String name, String data)
{
return "Content-Type: multipart/related; boundary=\"----\"\n" + "\n"
+ "------\n" + "Content-Location:" + name + "\n"
+ "Content-Transfer-Encoding:base64\n" + "\n" + data + "=\n"
+ "------\n";
}
//
// Handling of multipart/form-data *** NOT FOR PRODUCTION USE!! ***
//
/**
* Encoding for the multipart/form-data.
*/
protected static final String ENCODING = "ISO-8859-1";
/**
* Parses the given multipart/form-data request into a map that maps from
* names to values. Note that this implementation ignores the file type and
* filename and does only return the actual data as the value for the name
* of the file input in the form. Returns an empty map if the form does not
* contain any multipart/form-data.
*/
protected Map<String, String> parseMultipartRequest(
HttpServletRequest request) throws IOException
{
Map<String, String> result = new Hashtable<String, String>();
String contentType = request.getHeader("Content-Type");
// Checks if the form is of the correct content type
if (contentType != null
&& contentType.indexOf("multipart/form-data") == 0)
{
// Extracts the boundary from the header
int boundaryIndex = contentType.indexOf("boundary=");
String boundary = "--"
+ contentType.substring(boundaryIndex + 9).trim();
// Splits the multipart/form-data into its different parts
Iterator<String> it = splitFormData(
readStream(request.getInputStream()), boundary).iterator();
while (it.hasNext())
{
parsePart(it.next(), result);
}
}
return result;
}
/**
* Parses the values in the given form-data part into the given map. The
* value of the name attribute will be used as the name for the data. The
* filename will be stored under filename in the given map and the
* content-type is ignored in this implementation.
*/
protected void parsePart(String part, Map<String, String> into)
{
String[] lines = part.split("\r\n");
if (lines.length > 1)
{
// First line contains content-disposition in the following format:
// form-data; name="upfile"; filename="avatar.jpg"
String[] tokens = lines[1].split(";");
// Stores the value of the name attribute for the form-data
String name = null;
for (int i = 0; i < tokens.length; i++)
{
String tmp = tokens[i];
int index = tmp.indexOf("=");
// Checks if the token contains a key=value pair
if (index >= 0)
{
String key = tmp.substring(0, index).trim();
String value = tmp.substring(index + 2, tmp.length() - 1);
if (key.equals("name"))
{
name = value;
}
else
{
into.put(key, value);
}
}
}
// Last line contains the actual data
if (name != null)
{
into.put(name, lines[lines.length - 1]);
}
}
}
/**
* Returns the parts of the given multipart/form-data.
*/
protected List<String> splitFormData(String formData, String boundary)
{
List<String> result = new LinkedList<String>();
int nextBoundary = formData.indexOf(boundary);
while (nextBoundary >= 0)
{
if (nextBoundary > 0)
{
result.add(formData.substring(0, nextBoundary));
}
formData = formData.substring(nextBoundary + boundary.length());
nextBoundary = formData.indexOf(boundary);
}
return result;
}
/**
* Reads the complete stream into memory as a String.
*/
protected String readStream(InputStream is) throws IOException
{
if (is != null)
{
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try
{
Reader reader = new BufferedReader(new InputStreamReader(is,
ENCODING));
int n;
while ((n = reader.read(buffer)) != -1)
{
writer.write(buffer, 0, n);
}
}
finally
{
is.close();
}
return writer.toString();
}
else
{
return "";
}
}
}