ConverterServlet.java 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package com.mxgraph.online;
  2. import java.io.BufferedReader;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.io.OutputStream;
  8. import java.net.HttpURLConnection;
  9. import java.net.URI;
  10. import java.net.URL;
  11. import java.util.List;
  12. import java.util.logging.Level;
  13. import java.util.logging.Logger;
  14. import javax.servlet.ServletException;
  15. import javax.servlet.http.HttpServlet;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import org.apache.commons.fileupload.FileItem;
  19. import org.apache.commons.fileupload.FileUploadException;
  20. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  21. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  22. import java.nio.file.Path;
  23. import java.nio.file.Paths;
  24. //This servlet is an interface between draw.io and CloudConverter.
  25. //For EMF files, it detect its size and resize the huge images such that max dimension is MAX_DIM
  26. public class ConverterServlet extends HttpServlet
  27. {
  28. private static final long serialVersionUID = -5084595244442555865L;
  29. private static final Logger log = Logger
  30. .getLogger(HttpServlet.class.getName());
  31. private static final int MAX_DIM = 5000;
  32. private static final double EMF_10thMM2PXL = 26.458;
  33. private static final String API_KEY_FILE_PATH = "/WEB-INF/cloud_convert_api_key"; // Not migrated to new pattern, since will not be used on diagrams.net
  34. private static final String CONVERT_SERVICE_URL = "https://api.cloudconvert.com/convert";
  35. private static final String CRLF = "\r\n";
  36. private static final String TWO_HYPHENS = "--";
  37. private static final String BOUNDARY = "----WebKitFormBoundary6XTanBMjO0kFwa3p"; //FIXME The boundary should not occur inside the file, it is very unlikely but still a possibility
  38. private static String API_KEY = null;
  39. private void readApiKey()
  40. {
  41. if (API_KEY == null)
  42. {
  43. try
  44. {
  45. API_KEY = Utils
  46. .readInputStream(getServletContext()
  47. .getResourceAsStream(API_KEY_FILE_PATH))
  48. .replaceAll("\n", "");
  49. }
  50. catch (IOException e)
  51. {
  52. throw new RuntimeException("Invalid API key file/path");
  53. }
  54. }
  55. }
  56. //Little Indian
  57. private int fromByteArray(byte[] bytes, int start)
  58. {
  59. return ((bytes[start + 3] & 0xFF) << 24) |
  60. ((bytes[start + 2] & 0xFF) << 16) |
  61. ((bytes[start + 1] & 0xFF) << 8 ) |
  62. ((bytes[start] & 0xFF) << 0 );
  63. }
  64. /**
  65. * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
  66. */
  67. protected void doPost(HttpServletRequest request,
  68. HttpServletResponse response) throws ServletException, IOException
  69. {
  70. readApiKey();
  71. String inputformat = null, outputformat = null, fileName = null;
  72. InputStream fileContent = null;
  73. try
  74. {
  75. List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
  76. for (FileItem item : items)
  77. {
  78. if (item.isFormField())
  79. {
  80. String fieldName = item.getFieldName();
  81. if ("inputformat".equals(fieldName))
  82. {
  83. inputformat = item.getString();
  84. }
  85. else if ("outputformat".equals(fieldName))
  86. {
  87. outputformat = item.getString();
  88. }
  89. }
  90. else
  91. {
  92. //We expect only one file
  93. Path file = Paths.get(item.getName());
  94. fileName = file.getFileName().toString();
  95. fileContent = item.getInputStream();
  96. }
  97. }
  98. }
  99. catch (FileUploadException e)
  100. {
  101. throw new ServletException("Cannot parse multipart request.", e);
  102. }
  103. if (inputformat == null || outputformat == null || fileContent == null)
  104. {
  105. response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  106. }
  107. else
  108. {
  109. HttpURLConnection con = null;
  110. try
  111. {
  112. URL obj = new URL(CONVERT_SERVICE_URL);
  113. con = (HttpURLConnection) obj.openConnection();
  114. con.setUseCaches(false);
  115. con.setDoOutput(true);
  116. con.setRequestMethod("POST");
  117. con.setRequestProperty("Connection", "Keep-Alive");
  118. con.setRequestProperty("Cache-Control", "no-cache");
  119. con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
  120. DataOutputStream postRequest = new DataOutputStream(con.getOutputStream());
  121. byte[] data = new byte[10240]; //10 KB buffer
  122. int bytesRead = fileContent.read(data);
  123. int w = 0, h = 0, dpi = 96;
  124. if (inputformat.equals("emf") && bytesRead >= 40)
  125. {
  126. //Read Frame from EMF header (the rectangular inclusive-inclusive dimensions, in .01 millimeter units,
  127. // of a rectangle that surrounds the image stored in the metafile.)
  128. int x0 = fromByteArray(data, 24);
  129. int y0 = fromByteArray(data, 28);
  130. int x1 = fromByteArray(data, 32);
  131. int y1 = fromByteArray(data, 36);
  132. //Approximate dimensions of the image
  133. w = (int) ((x1 - x0) / EMF_10thMM2PXL);
  134. h = (int) ((y1 - y0) / EMF_10thMM2PXL);
  135. }
  136. if (w > MAX_DIM || h > MAX_DIM)
  137. {
  138. dpi = (int) (dpi * Math.min(MAX_DIM / (double) w, MAX_DIM / (double) h));
  139. if (dpi == 0)
  140. {
  141. dpi = 1;
  142. }
  143. }
  144. addParameter("apikey", API_KEY, postRequest);
  145. addParameter("inputformat", inputformat, postRequest);
  146. addParameter("outputformat", outputformat, postRequest);
  147. addParameter("input", "upload", postRequest);
  148. addParameter("wait", "true", postRequest);
  149. addParameter("download", "true", postRequest);
  150. if (dpi != 96)
  151. {
  152. addParameter("converteroptions[density]", Integer.toString(dpi), postRequest);
  153. }
  154. addParameterHeader("file", fileName, postRequest);
  155. while(bytesRead != -1)
  156. {
  157. postRequest.write(data, 0, bytesRead);
  158. bytesRead = fileContent.read(data);
  159. }
  160. postRequest.writeBytes(CRLF + TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + CRLF);
  161. postRequest.flush();
  162. postRequest.close();
  163. InputStream in = con.getInputStream();
  164. response.setStatus(con.getResponseCode());
  165. String contentType = "application/octet-stream";
  166. if ("png".equals(outputformat))
  167. {
  168. contentType = "image/png";
  169. }
  170. else if ("jpg".equals(outputformat))
  171. {
  172. contentType = "image/jpeg";
  173. }
  174. response.setHeader("Content-Type", contentType);
  175. OutputStream out = response.getOutputStream();
  176. bytesRead = in.read(data);
  177. try
  178. {
  179. URI uri = new URI(request.getHeader("referer"));
  180. String domain = uri.getHost();
  181. log.log(Level.CONFIG, "EMF-CONVERT, domain: " + domain + " ,Filename: " +
  182. fileName != null ? fileName : "" + ", size: " + bytesRead);
  183. }
  184. catch (Exception e)
  185. {
  186. e.printStackTrace();
  187. }
  188. while(bytesRead != -1)
  189. {
  190. out.write(data, 0, bytesRead);
  191. bytesRead = in.read(data);
  192. }
  193. in.close();
  194. out.flush();
  195. out.close();
  196. }
  197. catch(Exception e)
  198. {
  199. e.printStackTrace();
  200. if (con != null)
  201. {
  202. try
  203. {
  204. BufferedReader in = new BufferedReader(
  205. new InputStreamReader(con.getErrorStream()));
  206. String inputLine;
  207. while ((inputLine = in.readLine()) != null)
  208. {
  209. System.err.println(inputLine);
  210. }
  211. in.close();
  212. }
  213. catch (Exception e2)
  214. {
  215. // Ignore
  216. }
  217. }
  218. response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
  219. }
  220. }
  221. }
  222. private void addParameter(String name, String val, DataOutputStream postRequest) throws IOException {
  223. addParameterHeader(name, null, postRequest);
  224. postRequest.writeBytes(val);
  225. postRequest.writeBytes(CRLF);
  226. }
  227. private void addParameterHeader(String name, String fileName, DataOutputStream postRequest) throws IOException {
  228. postRequest.writeBytes(TWO_HYPHENS + BOUNDARY + CRLF);
  229. postRequest.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"" +
  230. (fileName != null? "; filename=\"" + fileName + "\"" + CRLF + "Content-Type: application/octet-stream" : "") + CRLF);
  231. postRequest.writeBytes(CRLF);
  232. }
  233. }