Saving HTML5 <canvas> Image w/ Java Servlet - java

I know there are a lot of StackOverflow questions about this already, but I've searched through as many as I could find and have yet to get my code working, so I am finally posting my own question.
My goal is to save an image that is on an HTML5 <canvas> in my webpage to a file on my server. I was hoping to accomplish this using a Java servlet.
My JavasScript grabs the canvas image data like this:
var canvas = document.getElementById("screenshotCanvas");
var context = canvas.getContext("2d");
var imageDataURL = canvas.toDataURL('image/png');
// I'm not if I need to do this, I've tried several different ways to no avail
//imageDataURL = imageDataURL.replace("image/png", "image/octet-stream");
//imageDataURL = imageDataURL.replace(/^data:image\/(png|jpeg);base64,/,"");
$.ajax({
url: screenshotCreateUrl,
type: "POST",
data: { imgBase64: imageDataURL },
error: function(jqXHR, textStatus, errorThrown) {
// Handle errors
},
success: function(data, textStatus, jqXHR) {
// Do some stuff
}
});
My Java servlet tries to save the image like so:
try {
HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request);
HttpServletRequestWrapper requestWithWrapper = (HttpServletRequestWrapper) wrappedRequest.getRequest();
byte[] contentData = requestWithWrapper.getContentData();
byte[] decodedData = Base64.decodeBase64(contentData);
FileOutputStream fos = new FileOutputStream("testOutput.png");
fos.write(decodedData);
fos.close();
} catch(Exception e) {
// Handle exceptions
}
The servlet successfully writes out an image file, but it does not open properly and does not contain all the image data in it. My Javascript successfully grabs the <canvas> image data, which looks like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAACWCAYAAABkW7XSAAAgAElEQVR4nJTa51NcaZ7ge+QBIZBAwgoEkvDeJt577733njRk4r3PhDRAJkkmiTdCXqWqUlVXtZl209OzvWN27t17/5rvvoCu7t6ZmN158YnfcyJOnIjz4vnGEyeOWdRmAP+RaI3fT2I0PiTtBJF9KKD8TTKVb5IpPosl+zCMdFMgKQY/otTPCFl3IljhSPiWB5E7XkToPAnVexNq8CVyP5Cwg0CCDwII2vfHx+TDC6MXz3df8mznGc7bTthu2PJwxRrrufvcnzbn4bQFjnNWuK3a4r3hQrDmBSGbLwlRvyR0w5OQ9ZeErHsRsuFLyIYvQeu+BCh88Zf74K/wxU8ZgL8qEH9VEAHq0L8RqAkjeCuCsO1IwrVRhGujCNNdCdVFEayNJEQXdUUfTYg+mmC9AH99BL47YfjuhOK7G4KvIRhfQzDee4F47QXibQrCe98f730/vPf98N3zw9/kT+B+IG . . . [and so on]
Any ideas what I am missing here? I feel like i am making some tiny mistake that I just can't spot.

Had the same task and was able to make it work (without jQuery and with the help of maclema's reply), by using multipart/form-data content type:
var xhr = new XMLHttpRequest();
xhr.open("post", "AddressOfYourServlet", false);
var boundary = Math.random().toString().substr(2);
xhr.setRequestHeader("content-type",
"multipart/form-data; charset=utf-8; boundary=" + boundary);
var multipart = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=myImg\r\n" +
"Content-type: image/png\r\n\r\n" +
canvas.toDataURL("image/png") + "\r\n" +
"--" + boundary + "--\r\n";
xhr.send(multipart);
To go asynchronously or if you have more parts to send (e.g. multiple images) or if you want to work with the response, see How to send multipart/form-data form content by ajax (no jquery)?
Your servlet's doPost method would look something like:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Part part = request.getPart("myImg");
BufferedReader br = new BufferedReader(new InputStreamReader(part.getInputStream(),
Charset.forName("utf-8")));
String sImg = br.readLine();
sImg = sImg.substring("data:image/png;base64,".length());
byte[] bImg64 = sImg.getBytes();
byte[] bImg = Base64.decodeBase64(bImg64); // apache-commons-codec
FileOutputStream fos = new FileOutputStream("img.png");
fos.write(bImg);
}
Hope this helps.

You want to get the post parameter and not the content data of the request. As well you will also need to strip the encoding information.
Try this:
try {
HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request);
HttpServletRequestWrapper requestWithWrapper = (HttpServletRequestWrapper) wrappedRequest.getRequest();
String imageString = wrappedRequest.getParameter("imgBase64");
imageString = imageString.substring("data:image/png;base64,".length);
byte[] contentData = imageString.getBytes();
byte[] decodedData = Base64.decodeBase64( contentData );
FileOutputStream fos = new FileOutputStream("testOutput.png");
fos.write(decodedData);
fos.close();
} catch(Exception e) {
// Handle exceptions
e.printStackTrace();
}

Related

Unexpected character encountered while parsing value when validating content as JSON in WOPI

I am currently implementing the WOPI with my application. Our domain is already whitelisted by Microsoft. While implementation I am currently facing two problems as mentioned below:
The exception is thrown when trying to validate content as JSON: 'Unexpected character encountered while parsing value.' I am sending my response "Value=application/octet-stream" but I don't understand why the server is trying to parse the stream as JSON.
After every new request coming from "iframe" is initiating a new session in the JAVA.
Here are more details:
My current URL is https://onenote.officeapps-df.live.com/hosting/WopiTestFrame.aspx?ui=en-US&rs=en-US&dchat=1&hid=26D7CA2A10F60A68720106BF599F84B9&&WOPISrc=https://domain/wopiEditor/files/73346e47-697b-11e6-a8bc-c26cd8f74b91/courses/independentConcepts/concept_adminGlo_5/assets/Setting url for static ip.docx&access_token=DEADBEEFDEADBEEFDEADBEEF&access_token_ttl=1532765580679
And My Java code is as following:
public void getFile(HttpServletRequest request, HttpServletResponse response, String name) {
Println.getInstance().log(request.getSession().getId() + "re" + request.getRequestURI());
InputStream fis = null;
OutputStream toClient = null;
try {
String path = getFilePath(request) + name;
File file = new File(path);
String filename = file.getName();
// XWPFDocument xDoc = new XWPFDocument(OPCPackage.open(fis));
fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
response.reset();
response.addHeader("Content-Disposition",
"attachment;filename=" + new String(filename.getBytes("utf-8"), "ISO-8859-1"));
response.addHeader("Content-Length", "" + file.length());
response.addHeader("Content-Type", "" + "application/octet-stream");
//Println.getInstance().log(file.length() + "l" + file);
toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
fis.close();
toClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The test frame image is attached
The error you are seeing is on the CheckFileInfo request which is supposed to be returned as JSON. The Java snippit that you provided is for the getFile request which is a separate call that is made from the Office Online server. You should look over https://wopi.readthedocs.io/projects/wopirest/en/latest/ for how to write your implementation.
One thought is maybe you need to set the Content-Type header more specifically instead of the application/octet-stream you are sending?
Also there are quite a lot of other header values you are supposed to be returning, some of them may matter as well:
https://wopi.readthedocs.io/projects/wopirest/en/latest/common_headers.html#common-headers

Large file does not download with jsp

I am sending an AJAX POST request from the script inside JSP.
Inside controller i am reading the file from the location and return the byte Array.
fileInputStreamReader = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
fileInputStreamReader.read(bytes);
filedata = Base64.getEncoder().encode(bytes);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentLength((int) file.length());
fileInputStreamReader.close();
Then on the front end i am crating an invisible link and downloading the file.
$.ajax({
url : url,
type : 'POST',
data : nodedata,
beforeSend : function(jqXHR, settings) {
setCsrfHeader(jqXHR);
},
success : function(data) {
hideLoader();
/* window.open("data:"+contentType+";base64, " + data); */
var uri = 'data:'+contentType+';base64,' + data;
var downloadLink = document.createElement("a");
downloadLink.href = uri;
downloadLink.download =atcName ;
document.body.appendChild(downloadLink);
downloadLink.click();
$("#Success").html("File download successful");
$("#error").hide();
$("#Success").show();
document.body.removeChild(downloadLink);
},
error : function(e) {
hideLoader();
$("#error").html(ERROR_SERVER_RESPONSE);
$("#Success").hide();
$("#error").show();
alert(ERROR_SERVER_RESPONSE);
}
});
The problem is only with large file size >~50mb. What should I do?
I might be wrong but I suggest to check if there is no cache issue because a timeout should trigger an error callback.
You should add the aparameter cache : false, in your request or, better IMHO, you can prevent all futher Ajaxs call from being cached, regardless of which jQuery method you use(ajax, get...).
$.ajaxSetup({ cache: false });

Upload file stop with Unexpected EOF read on the socket Exception

Sometime when I try to upload a file on my remote vps i get this exception (the upload proccess stop in 60%)
06-Jan-2016 11:59:36.801 SEVERE [http-nio-54000-exec-9] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [mvc-dispatcher] in context with path [] threw exception [Request processing failed;
nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request;
nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. Unexpected EOF read on the socket]
with root cause
java.io.EOFException: Unexpected EOF read on the socket
and in Google Chrome the connextion is lost like the server is down, i get ERR_CONNECTION_ABORTED
i upload file like this in spring mvc
public void save_file(MultipartFile upfile , String path){
try {
File fichier = new File( path ) ;
byte[] bytes = upfile.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream( fichier ));
stream.write(bytes);
stream.close();
System.out.println( "You successfully uploaded " + upfile.getOriginalFilename() + "!" );
} catch (Exception e) {
System.out.println( "You failed to upload " + upfile.getOriginalFilename() + " => " + e.getMessage() ); ;
}
}
my controller :
#RequestMapping(value = "/administration/upload", method = RequestMethod.POST)
public String Upload_AO_journal(
#ModelAttribute UploadForm uploadForm,
Model map , HttpServletRequest request, HttpSession session ) throws ParseException, UnsupportedEncodingException {
my bean
public class UploadForm {
...
public MultipartFile scan;
So how can solve this problem ?
Have you tried stream?
Jsp code:
<form method="POST" onsubmit="" ACTION="url?${_csrf.parameterName}=${_csrf.token}" ENCTYPE="multipart/form-data">
Controller:
#RequestMapping(
value = "url", method = RequestMethod.POST
)
public void uploadFile(
#RequestParam("file") MultipartFile file
) throws IOException {
InputStream input = upfile.getInputStream();
Path path = Paths.get(path);//check path
OutputStream output = Files.newOutputStream(path);
IOUtils.copy(in, out); //org.apache.commons.io.IOUtils or you can create IOUtils.copy
}
All that worked for me with spring 4.0 and spring security.
Secondly, you should check if the http connection is timeout. Chrome does not support that configuration. So you can use firefox and follow here http://morgb.blogspot.com.es/2014/05/firefox-29-and-http-response-timeout.html.
Not sure about the getBytes() method on the upfile. A more suitable approach would be to use the InputStream which will manage any size file and will buffer when necessary. Something like:
public void save_file(MultipartFile upfile , String path){
try {
File fichier = new File( path ) ;
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream( fichier ));
InputStream is = upfile.getInputStream();
byte [] bytes = new byte[1024];
int sizeRead;
while ((sizeRead = is.read(bytes,0, 1024)) > 0) {
stream.write(bytes, 0, sizeRead);
}
stream.flush();
stream.close();
System.out.println( "You successfully uploaded " + upfile.getOriginalFilename() + "!" );
} catch (Exception e) {
System.out.println( "You failed to upload " + upfile.getOriginalFilename() + " => " + e.getMessage() ); ;
}
}
This issue appears because you close stream until stream write whole data.
Wrong way:
stream.write(bytes);
stream.close();
Right way:
try (BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(fichier)))
{
stream.write(data);
}
You should close your stream after whole data is written.
I had similar issues and problem is when you are uploading file you are using Multipart Form POST Request. You can read about MIME .
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=frontier
This is a message with multiple parts in MIME format.
--frontier
Content-Type: text/plain
This is the body of the message.
--frontier
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg
Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==
--frontier--
Basically issue I had was that multipart from request has meta information part, text part and actual file encoded I believe in base64. Each of this parts are split by boundary. If you don't set up this boundary (in example above it's called "frontier") correctly server starts reading the file but doesn't know where it ends until it reaches EOF and returns error about unexpected EOF because it didn't found required boundaries.
Problem with your code is that you are writing file into ByteOutputStream which is suitable when returning file from server to the user not other way around. Server is expecting this Multipart request with some standard predefined formatting. Use Apache Commons HttpClient library that does all this request formating and boundary setting for you. If you use Maven then this link.
File f = new File("/path/fileToUpload.txt");
PostMethod filePost = new PostMethod("http://host/some_path");
Part[] parts = {
new StringPart("param_name", "value"),
new FilePart(f.getName(), f)
};
filePost.setRequestEntity(
new MultipartRequestEntity(parts, filePost.getParams())
);
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
Disclaimer: code is not mine it's example from Apache website for multipart request
I got the same error when I didn't properly set the path where the file is going to be placed.
The fix was to change it like this:
factoryMaster.setCertificateFile("E:\\Project Workspace\\Live Projects\\fileStore\\Factory Master\\"+factoryMasterBean.getFile().getOriginalFilename());
and use throws exception in controller:
public #ResponseBody ResponseEntity<FactoryMaster> saveFactoryMaster(#ModelAttribute("factoryMasterBean") FactoryMasterBean factoryMasterBean,FactoryMaster factoryMaster) throws IllegalStateException, IOException {...............}
and make sure do not send any file with the same name which already exists.

How to send string compressed with GZIP from Java App to PHP web service

I have this issue with GZIP compression:
I need to send by POST method a huge JSON string, which is too big to be accept like URL (Ex: http://localhost/app/send/JSON STRING ENCODED BY BASE64), than it result in HTTP error 403
so, I need to compress my json and I found a way to do it with GZIP compression, which I can decompress with gzdecode() in PHP.
but it doesn't work...
my functions compress() and decompress() works fine inside my Java App, but when I send it to webservice, something goes wrong and gzdecode() doesn't work.
I have no idea what I missing, I need some help
functions used in java app (client)
public String Post(){
String retorno = "";
String u = compress(getInput());
u = URLEncoder.encode(URLEncoder.encode(u, "UTF-8"));
URL uri = new URL(url + u);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setDoOutput(false);
conn.setRequestMethod(getMethod());
conn.setRequestProperty("Content-encoding", "gzip");
conn.setRequestProperty("Content-type", "application/octet-stream");
BufferedReader buffer = new BufferedReader(
new InputStreamReader((conn.getInputStream())));
String r = "";
while ((r = buffer.readLine()) != null) {
retorno = r + "\n";
}
return retorno;
}
GZIP compress function (client)
public static String compress(String str) throws IOException {
byte[] blockcopy = ByteBuffer
.allocate(4)
.order(java.nio.ByteOrder.LITTLE_ENDIAN)
.putInt(str.length())
.array();
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
gos.close();
os.close();
byte[] compressed = new byte[4 + os.toByteArray().length];
System.arraycopy(blockcopy, 0, compressed, 0, 4);
System.arraycopy(os.toByteArray(), 0, compressed, 4,
os.toByteArray().length);
return Base64.encode(compressed);
}
method php used to receive a URL (server, using Slim/PHP Framework)
init::$app->post('/enviar/:obj/', function( $obj ) {
$dec = base64_decode(urldecode( $obj ));//decode url and decode base64 tostring
$dec = gzdecode($dec);//here is my problem, gzdecode() doesn't work
}
post method
public Sender() throws JSONException {
//
url = "http://192.168.0.25/api/index.php/enviar/";
method = "POST";
output = true;
//
}
As noticed in some of the comments.
Bigger data should be send as a POST request instead of GET. URL params should be used only for single variables. As you noticed the URL length is limited to few kB and it's not very good idea to send larger data this way (even though GZIP compressed).
Your GZIP compression code seems to be wrong. Please try this:
public static String compress(String str) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
GZIPOutputStream gos = new GZIPOutputStream(os);
gos.write(str.getBytes());
os.close();
gos.close();
return Base64.encodeToString(os.toByteArray(),Base64.DEFAULT);
}

ajax response as pdf, how to show it?

I have a web service that generate a pdf. In my GAE application I have a button, when i click i use an ajax's function.
$('#test').click(function(){
$.ajax({
url: 'provaws.do',
type: 'get',
dataType: 'html',
success : function(data) {
}
});
});
this is the method in java that's call ws, using UrlFetch:
#RequestMapping(method = RequestMethod.GET, value = PROVAWS_URL)
public void prova(HttpServletRequest httpRequest, HttpServletResponse httpResponse, HttpSession httpSession) throws IOException{
try {
URL url = new URL("http://XXXXX/sap/bc/zcl_getpdf/vbeln/yyyyyy");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Authorization","Basic " + Base64.encodeBase64String(("username:password").getBytes()));
connection.setConnectTimeout(60000);
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// OK
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream is = null;
try {
is = connection.getInputStream();
byte[] byteChunk = new byte[4096];
int n;
while ( (n = is.read(byteChunk)) > 0 ) {
bais.write(byteChunk, 0, n);
}
}
catch (IOException e) {
}
finally {
if (is != null) { is.close(); }
}
httpResponse.setContentType("application/pdf");
httpResponse.setHeader("content-disposition","attachment; filename=yyyyy.pdf");
httpResponse.getOutputStream().write(bais.toString().getBytes("UTF-8"));
httpResponse.getOutputStream().flush();
}
....
}
With Firebug i see the repsonse:
%PDF-1.3
%âãÏÓ
2 0 obj
<<
/Type /FontDescriptor
/Ascent 720
/CapHeight 660
/Descent -270
/Flags 32
/FontBBox [-177 -269 1123 866]
/FontName /Helvetica-Bold
/ItalicAngle 0
....
What i need to set in ajax's function to show the pdf?
Thanks in advance
I don't know Java well, but in my understanding your mechanism may not be right.
Here are my corrections:
Instead of sending files in stream, the server-side code(JAVA) should generate the pdf at backend, put the file in file system, and then return the URI of file to Ajax response.
For Ajax code, it get the url from server, then show the new url in DOM. Then user can follow this link to read/download PDF.
Side note:
I checked further that there are methods for streaming data by Ajax, though jQuery's ajax() can't handle that. But I think for a PDF file rendering, streaming is overkill.
Refs: jquery ajax, read the stream incrementally?, http://ajaxpatterns.org/HTTP_Streaming#In_A_Blink*

Categories

Resources