gzip/deflate compression with Server Sent Events on tomcat servlet - java

I am using server sent events for streaming responses (text/event-stream). We want to compress the response using gzip or deflate compression, but the browser shows : ERR_CONTENT_DECODING_FAILED.
Code for the same -
response.setContentType("text/event-stream; charset=UTF-8");
response.addHeader("Connection", "keep-alive");
response.addHeader("Cache-Control", "no-cache, must-revalidate");
response.addHeader("Content-Encoding", "deflate");
PrintWriter writer = response.getWriter();
number = 10;
time = 100;
for (int i = 0; i < number; i++) {
String resp = "data: " + "Some Response" + "\r\n";
Deflater deflater = new Deflater(Deflater.DEFLATED);
byte[] input = resp.getBytes("UTF-8");
deflater.setInput(input);
deflater.finish();
byte[] output = new byte[1024];
deflater.deflate(output);
deflater.end();
writer.write(new String(output, "UTF-8"));
writer.flush();
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String resp = "data: close\r\n";
Deflater deflater = new Deflater(Deflater.DEFLATED);
byte[] input = resp.getBytes("UTF-8");
deflater.setInput(input);
deflater.finish();
byte[] output = new byte[1024];
deflater.deflate(output);
deflater.end();
writer.write(new String(output, "UTF-8"));
writer.flush();

Compressed stream is binary data. It must not be printed with response.getWriter(). Use response.getOutputStream() instead.
UPDATE
Shiva Bhalla: Using the response.getOutputStream() with text/event-stream isn't doing proper streaming after the 1st chunk of response is displayed, the request is being failed at the browser.
You have to compress the whole stream using a single Deflater instance.
In your code you compress each fragment separately. This is equivalent to the following:
You take a series of text files (01.txt, 02.txt, ...).
You compress each of them into a gzip archive. (01.txt.gz, 02.txt.gz, ...)
You concatenate the archives into a single file.
The above produces an incorrect archive. Gzip files cannot be concatenated like that. The correct code should do the following:
You take a series of text files (01.txt, 02.txt, ...).
You concatenate the text files into a single file (text.txt).
You compress the file into a gzip archive. (text.txt.gz)
I recommend you to use a java.util.zip.DeflaterOutputStream (1) instead of direct use of a Deflater. E.g.
new DeflaterOutputStream(response.getOutputStream(), true);
Beware of introducing a BREACH vulnerability.

Adding "text/event-stream;charset=UTF-8,ms-stream;charset=UTF-8" as "compressableMimeType" in connector properties did the job!

Related

How send the http post request with DataOutPutStream properly so server can handle it

I wish somebody tell me what is the different of just write a file and a file with another kind of bytes.
server using, python3 flask
I think maybe the android retrofit etc useful, but I would like to try with the classic method, HTTPUrlConnection
So i successfully sending just one or multi-parameter of string to the server.
I also successfully just by sending a file to the server.
- my file will just 5-second audio or video mp4 that creates from real android.
When i tried just two, param and a list of byte, len(list) = 2, i can get back my sent file, but the concat style of the bytes just could not acheive it.
but when I combine both of it, i found out that when the file is chopped as multi-part, the file just could not recover.
I know delimeter is useful, I tried with a string of "--------------" to split it in server-side.
list= request.data.split(b"------------------------------")
newList= list[1:]
data = b""
for part in newList:
data += part
how i recover the file (python)
def createAudioFromDataReceived(fileName, data):
with open(fileName, 'wb') as wfile:
wfile.write(data)
the basic code write to dataOutPutStream
public void writeFilesParamToDataOutputStream(HttpURLConnection conn, File file, String action) throws IOException {
byte[] buffer;
FileInputStream fileInputStream = new FileInputStream(file);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
buffer = new byte[1024 * 1024];
int length = 0;
while ( ( length = fileInputStream.read( buffer ) ) > 0 ) {
dos.write(buffer, 0, length);
}
dos.flush();
fileInputStream.close();
dos.close();
}
to add extra line to the dataOutputStream
//Bytes
byte[] bytes = "toSend".getBytes();
dos.write(bytes);
dos.write("------------------------------");
ops, reference are seen before
roughly this kind How to send data from server to Android?
i could not imagine the byte got alot of "-" and need "/r/n" ...
delimiter should be some thing like
String delimiter = "--aaWEdFXvDF--" + "\r\n";

Java: Binary File Upload using Restlet + Apache Commons FileUpload

I have a REST API with Restlet 2.3 and need to implement a file-uploading functionality to it.
The problem is that, when someone uploads a file using a POST (with a multipart/form-data Content-Type), the file reaches the server with another encoding.
To test this, I printed the contents of the original file in a Unix Terminal and then printed it again before parsing the requet with Apache Commons FileUpload (with almost the same code of this example http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/fileupload). Both printed contents are very similar, but the original file has less characters, so i assume that my Java server is using the wrong encoding to interpret the file.
The file I sent is a PNG image. With text files the server works perfectly, but when I send photos or any binary file, the problem appears.
I don't know how you exactly did to check the received content. First you should check the content type that is used for your file part within the content of your multipart request. You should have something like that for a JPG image:
-----------------------------75956101888331271337088331
Content-Disposition: form-data; name="fileToUpload"; filename="myimage.jpg"
Content-Type: image/jpeg
Secondly, I don't know how you actually write the content you received. Apache Commons IO brings an utility method IOUtils.copy that provides a simple solution to write in an OutputStream the content received from an InputStream. See how ti can be used in your context:
while (fileIterator.hasNext()) {
FileItemStream fi = fileIterator.next();
if (fi.getFieldName().equals("fileToUpload")) {
FileOutputStream fos = new FileOutputStream(
"output"+File.separator+fi.getFieldName());
IOUtils.copy(fi.openStream(), fos);
fos.close();
}
}
IMO, the encoding aspect only applies for text not for binary content.
Hope it helps,
Thierry
I actually solved it by using Google's ByteStreams class:
while (fileIterator.hasNext()) {
FileItemStream fi = fileIterator.next();
if (fi.getFieldName().equals(FILE_TO_UPLOAD)) {
byte[] byteArray = ByteStreams.toByteArray(fi.openStream());
result = new String(byteArray,Charset.forName("ISO-8859-1"));
}
}
I had the similar problem when uploading the image file. This is how I fixed. The problem was in my case the data read from the inputstream. As it is reading from a socket no guarantee that you will have the full buffer of your array filled. Therefore you should check your data size before writing it to the outputbuffer/file. Here is my code hope it helps. Also available in repository https://github.com/esabilbulbul/java-servlet-fileupload/blob/master/README.md
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
upload.setHeaderEncoding("UTF-8");
// Parse the request
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext())
{
FileItemStream item = iter.next();
String name = item.getFieldName();
//InputStream attachmentStream = item.openStream();
//byte[] attachmentBytes = ByteStreams.toByteArray(attachmentStream);
//InputStream stream = item.getInputStream();
InputStream stream = item.openStream();
if (item.isFormField())
{
//System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
}
else
{
System.out.println("File field " + name + " with file name "+ item.getName() + " detected.");
// Process the input stream
FileOutputStream fout= new FileOutputStream ("c:\\" + item.getName());
BufferedOutputStream bout= new BufferedOutputStream (fout);
BufferedInputStream bin= new BufferedInputStream(stream);
byte buf[] = new byte[2048];
int len=0;
while ((len = bin.read(buf)) > 0)//((bin.read(buf)) != -1)
{
bout.write(buf, 0, len);
if (len<2048)
len = len;
}
bout.close();
bin.close();
}
}

Sending Large Image in chunks

I am sending images from my android client to java jersey restful service and I succeded in doing that.But my issue is when I try to send large images say > 1MB its consumes more time so I like to send image in CHUNKS can anyone help me in doing this.How to send(POST) image stream in CHUNKS to server
references used :
server code & client call
server function name
/*** SERVER SIDE CODE****/
#POST
#Path("/upload/{attachmentName}")
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
public void uploadAttachment(
#PathParam("attachmentName") String attachmentName,
#FormParam("input") InputStream attachmentInputStream) {
InputStream content = request.getInputStream();
// do something better than this
OutputStream out = new FileOutputStream("content.txt");
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
// whatever processing you want here
out.write(buffer, 0, len);
}
out.close();
return Response.status(201).build();
}
/**********************************************/
/**
CLIENT SIDE CODE
**/
// .....
client.setChunkedEncodingSize(1024);
WebResource rootResource = client.resource("your-server-base-url");
File file = new File("your-file-path");
InputStream fileInStream = new FileInputStream(file);
String contentDisposition = "attachment; filename=\"" + file.getName() + "\"";
ClientResponse response = rootResource.path("attachment").path("upload").path("your-file-name")
.type(MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", contentDisposition)
.post(ClientResponse.class, fileInStream);
You should split the file in the client and restore part of the file in the server.
and after that you should merge the files together. Take a look at split /merge file on coderanch
Enjoy ! :)
Another path is available, if you don't want to code too much consider using :
file upload apache that is great ! :)

How to upload binary file using URLConnection

In order to upload a binary file to an URL, I have been advised to use this guide. However, the file is not in a directory, but is stored in a BLOB field in MySql db. The BLOB field is mapped as a byte[] property in JPA:
byte[] binaryFile;
I have slightly modified the code taken from the guide, in this way:
HttpURLConnection connection = (HttpURLConnection ) new URL(url).openConnection();
// set some connection properties
OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, CHARSET), true);
// set some headers with writer
InputStream file = new ByteArrayInputStream(myEntity.getBinaryFile());
System.out.println("Size: " + file.available());
try {
byte[] buffer = new byte[4096];
int length;
while ((length = file.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
writer.append(CRLF).flush();
writer.append("--" + boundary + "--").append(CRLF).flush();
}
// catch and close streams
I am not using chunked streaming. The headers used are:
username and password
Content-Disposition: form-data; name=\"file\"; filename=\"myFileName\"\r\nContent-Type: application/octet-stream"
Content-Transfer-Encoding: binary
All the headers are received correctly by the host. It also receives the uploaded file, but unfortunately complains that the file is not readable, and asserts that the size of the received file is 37 bytes larger than the size outputed by my code.
My knowledge of streams, connections and byte[] is too limited for grasping the way to fix this. Any hints appreciated.
EDIT
As suggested by the commenter, I have tried also to write the byte[] directly, without using the ByteArrayInputStream:
output.write(myEntity.getBinaryFile());
Unfortunately the host gives exactly the same answer as the other way.
My code was correct.
The host was giving an error because it didn't expect the Content-Transfer-Encoding header. After removing it, everything went fine.

how to decompress http response?

Can any one of you solve this problem !
Problem Description:
i have received content-encoding: gzip header from http web-server.
now i want to decode the content but when i use GZIP classes from jdk 1.6.12, it gives null.
does it means that contents are not in gzip format ? or are there some another classes for decompress http response content?
Sample Code:
System.out.println("Reading InputStream");
InputStream in = httpuc.getInputStream();// httpuc is an object of httpurlconnection<br>
System.out.println("Before reading GZIP inputstream");
System.out.println(in);
GZIPInputStream gin = new GZIPInputStream(in));
System.out.println("After reading GZIP inputstream");
Output:
Reading InputStream
Before reading GZIP inputstream
sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#8acf6e
null
I have found one error in code, but don't able to understand it properly. what does it indicates.
Error ! java.io.EOFException
Thanks
I think you should have a look at HTTPClient, which will handle a lot of the HTTP issues for you. In particular, it allows access to the response body, which may be gzipped, and then you simply feed that through a GZIPInputStream
e.g.
Header hce = postMethod.getResponseHeader("Content-Encoding");
InputStream in = null;
if(null != hce)
{
if(hce.getValue().equals(GZIP)) {
in = new GZIPInputStream(postMethod.getResponseBodyAsStream());
}
// etc...
I second Brian's suggestion. Whenever u need to deal with getting/posting stuff via HTTP don't bother with low-level access use the Apache HTTP client.
InputStream is = con.getInputStream();
InputStream bodyStream = new GZIPInputStream(is);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int length;
while ((length = bodyStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
String body = new String(outStream.toByteArray(), "UTF-8");

Categories

Resources