Java Data stream decompression - java

get data from socket (buffer)
BufferedReader is = new BufferedReader(new InputStreamReader(download.getInputStream()));
data decompression
Inflater decompresser = new Inflater();
decompresser.setInput(buffer.toString().getBytes());
byte[] dataBytes = new byte[8388608];
int resultLength = decompresser.inflate(dataBytes);
decompresser.end();
System.out.println("decompressed" + new String(dataBytes, 0, resultLength)+ " RESULTLENGHT " +resultLength);
Sending 1000 bytes, compressed ZLIB and turns (800-900 bytes)
But I do not know what exactly the size of sending. I need to read from socket 1 byte at a time and immediately unpack it until the total size of decompressed data becomes equal to 1000 bytes.
To read a byte at a time I do so:
StringBuilder buffer = new StringBuilder();
StringBuilder unpackbuffer = new StringBuilder();
do buffer.append((char)is.read());
while(buffer.charAt(buffer.length()-1) != '|' && (byte)buffer.charAt(buffer.length()-1) != -1);
How do I fill in this cycle unpackbuffer? and check its size?
Sorry, I hope my question is clear.

OK, if you can't prepend the length to the data you're sending and then read in at the other end (which would obviously be the simplest ideal solution if you were able to design the protocol to allow this), then you can compress the stream 'byte by byte'. The trick is to create a 1-byte buffer as the input buffer. The code then looks as follows:
Inflater infl = new Inflater();
byte[] buf = new byte[1];
byte[] outputBuf = new byte[512];
while (!infl.finished()) {
while (infl.needsInput()) {
buf[0] = ...next byte from stream...
infl.setInput(buf);
}
int noUnc = infl.inflate(outputBuf);
// the first "noUnc" bytes of outputBuf contain the next bit of data
}

Related

Trying to use BufferedInputStream and Base64 to Encode a large file in Java

I am new to the Java I/O so please help.
I am trying to process a large file(e.g. a pdf file of 50mb) using the apache commons library.
At first I try:
byte[] bytes = FileUtils.readFileToByteArray(file);
String encodeBase64String = Base64.encodeBase64String(bytes);
byte[] decoded = Base64.decodeBase64(encodeBase64String);
But knowing that the
FileUtils.readFileToByteArray in org.apache.commons.io will load the whole file into memory, I try to use BufferedInputStream to read the file piece by piece:
BufferedInputStream bis = new BufferedInputStream(inputStream);
StringBuilder pdfStringBuilder = new StringBuilder();
int byteArraySize = 10;
byte[] tempByteArray = new byte[byteArraySize];
while (bis.available() > 0) {
if (bis.available() < byteArraySize) { // reaching the end of file
tempByteArray = new byte[bis.available()];
}
int len = Math.min(bis.available(), byteArraySize);
read = bis.read(tempByteArray, 0, len);
if (read != -1) {
pdfStringBuilder.append(Base64.encodeBase64String(tempByteArray));
} else {
System.err.println("End of file reached.");
}
}
byte[] bytes = Base64.decodeBase64(pdfStringBuilder.toString());
However, the 2 decoded bytes array don't look quite the same... ... In fact, the only give 10 bytes, which is my temp array size... ...
Can anyone please help:
what am I doing it wrong to read the file piece by piece?
why is the decoded byte array only returns 10 bytes in the 2nd solution?
Thanks in advance:)
After some digging, it turns out that the byte array's size has to be multiple of 3 in order to avoid padding. After using a temp array size with multiple of 3, the program is able to go through.
I simply change
int byteArraySize = 10;
to be
int byteArraySize = 1024 * 3;

Java Reading large files into byte array chunk by chunk

So I've been trying to make a small program that inputs a file into a byte array, then it will turn that byte array into hex, then binary. It will then play with the binary values (I haven't thought of what to do when I get to this stage) and then save it as a custom file.
I studied a lot of internet code and I can turn a file into a byte array and into hex, but the problem is I can't turn huge files into byte arrays (out of memory).
This is the code that is not a complete failure
public void rundis(Path pp) {
byte bb[] = null;
try {
bb = Files.readAllBytes(pp); //Files.toByteArray(pathhold);
System.out.println("byte array made");
} catch (Exception e) {
e.printStackTrace();
}
if (bb.length != 0 || bb != null) {
System.out.println("byte array filled");
//send to method to turn into hex
} else {
System.out.println("byte array NOT filled");
}
}
I know how the process should go, but I don't know how to code that properly.
The process if you are interested:
Input file using File
Read the chunk by chunk of the file into a byte array. Ex. each byte array record hold 600 bytes
Send that chunk to be turned into a Hex value --> Integer.tohexstring
Send that hex value chunk to be made into a binary value --> Integer.toBinarystring
Mess around with the Binary value
Save to custom file line by line
Problem:: I don't know how to turn a huge file into a byte array chunk by chunk to be processed.
Any and all help will be appreciated, thank you for reading :)
To chunk your input use a FileInputStream:
Path pp = FileSystems.getDefault().getPath("logs", "access.log");
final int BUFFER_SIZE = 1024*1024; //this is actually bytes
FileInputStream fis = new FileInputStream(pp.toFile());
byte[] buffer = new byte[BUFFER_SIZE];
int read = 0;
while( ( read = fis.read( buffer ) ) > 0 ){
// call your other methodes here...
}
fis.close();
To stream a file, you need to step away from Files.readAllBytes(). It's a nice utility for small files, but as you noticed not so much for large files.
In pseudocode it would look something like this:
while there are more bytes available
read some bytes
process those bytes
(write the result back to a file, if needed)
In Java, you can use a FileInputStream to read a file byte by byte or chunk by chunk. Lets say we want to write back our processed bytes. First we open the files:
FileInputStream is = new FileInputStream(new File("input.txt"));
FileOutputStream os = new FileOutputStream(new File("output.txt"));
We need the FileOutputStream to write back our results - we don't want to just drop our precious processed data, right? Next we need a buffer which holds a chunk of bytes:
byte[] buf = new byte[4096];
How many bytes is up to you, I kinda like chunks of 4096 bytes. Then we need to actually read some bytes
int read = is.read(buf);
this will read up to buf.length bytes and store them in buf. It will return the total bytes read. Then we process the bytes:
//Assuming the processing function looks like this:
//byte[] process(byte[] data, int bytes);
byte[] ret = process(buf, read);
process() in above example is your processing method. It takes in a byte-array, the number of bytes it should process and returns the result as byte-array.
Last, we write the result back to a file:
os.write(ret);
We have to execute this in a loop until there are no bytes left in the file, so lets write a loop for it:
int read = 0;
while((read = is.read(buf)) > 0) {
byte[] ret = process(buf, read);
os.write(ret);
}
and finally close the streams
is.close();
os.close();
And thats it. We processed the file in 4096-byte chunks and wrote the result back to a file. It's up to you what to do with the result, you could also send it over TCP or even drop it if it's not needed, or even read from TCP instead of a file, the basic logic is the same.
This still needs some proper error-handling to work around missing files or wrong permissions but that's up to you to implement that.
A example implementation for the process method:
//returns the hex-representation of the bytes
public static byte[] process(byte[] bytes, int length) {
final char[] hexchars = "0123456789ABCDEF".toCharArray();
char[] ret = new char[length * 2];
for ( int i = 0; i < length; ++i) {
int b = bytes[i] & 0xFF;
ret[i * 2] = hexchars[b >>> 4];
ret[i * 2 + 1] = hexchars[b & 0x0F];
}
return ret;
}

Error Sending ONLY Image/Video file

I'm trying to share files between my pc & android device over wifi. Strange thing is text and audio files are being perfectly transferred, while the images and video files fail to render.
Here's the main piece of code:
Sender:
BufferedOutputStream writer = new BufferedOutputStream(client.getOutputStream());
InputStream stream = new FileInputStream(file);
while ((read = stream.read(buffer)) != -1)
writer.write(buffer, 0, read);
Receiver:
BufferedInputStream input = new BufferedInputStream(client.getInputStream());
OutputStream outputStream = new FileOutputStream(file, true);
while (!store.complete) {
read = input.read(buffer, 0 , MAX_SIZE_OF_BUFFER);
String data = new String(buffer);
// Need to do some data manipulation (like removing a string which i appended
// while sending )
outputStream.write(data.getBytes());
}
Note: Even the size of the image/video file at the receiver is equal to the file being sent, still it fails to render.
Thanks in Advance!! :)
You can't assume that converting a byte[] to String and then converting it back to byte[] will give you the original byte[].
For example, the following does not output the original byte array:
byte[] buffer = {-127};
for (byte b : new String(buffer).getBytes()) { System.out.print(" " + b); }
To solve your problem, do your data manipulation (appending and removing your data) as bytes and don't convert to and from String.

Reading char[] from web-service by HttpGet — strange behavior

I am developing an Android application, it is going to fetch a big chunk of JSON data in stream. Calling the web service is OK, but I have a little problem. In my old version I was using Gson for reading the stream then I've tried to insert data to database, it was OK without any problem except performance. So I tried to change approach of loading data, I am trying to read data to char[] first then insert them to database.
This is my new code:
HttpEntity responseEntity = response.getEntity();
final int contentLength = (int) responseEntity.getContentLength();
InputStream stream = responseEntity.getContent();
InputStreamReader reader = new InputStreamReader(stream);
int readCount = 10 * 1024;
int hasread = 0;
char[] buffer = new char[contentLength];
int mustWrite = 0;
int hasread2 = 0;
while (hasread < contentLength) {
// problem is here
hasread += reader.read(buffer, hasread, contentLength - hasread);
}
Reader reader2 = new CharArrayReader(buffer);
The problem is that the reader starts reading correctly but at near of the end of stream, the hasread variable value decreases (by 1) instead of increasing. Very strange to me, and then the while loop never finishes. What's wrong with this code?
You should use a fixed size for the buffer, not the size of whole data (the contentLength). And an important note: the length of a char[] array is different to byte[] array's. The char data type is a single 16-bit Unicode character. While the byte data type is an 8-bit signed two's complement integer.
Also your while loop is wrong, you can fix it as:
import java.io.BufferedInputStream;
private static final int BUF_SIZE = 10 * 1024;
// ...
HttpEntity responseEntity = response.getEntity();
final int contentLength = (int) responseEntity.getContentLength();
InputStream stream = responseEntity.getContent();
BufferedInputStream reader = new BufferedInputStream(stream);
int hasread = 0;
byte[] buffer = new byte[BUF_SIZE];
while ((hasread = reader.read(buffer, 0, BUF_SIZE)) > 0) {
// For example, convert the buffer to a String
String data = new String(buffer, 0, hasread, "UTF-8");
}
Make sure to use your own charset ("UTF-8", "UTF-16"…).

Reading a binary input stream into a single byte array in Java

The documentation says that one should not use available() method to determine the size of an InputStream. How can I read the whole content of an InputStream into a byte array?
InputStream in; //assuming already present
byte[] data = new byte[in.available()];
in.read(data);//now data is filled with the whole content of the InputStream
I could read multiple times into a buffer of a fixed size, but then, I will have to combine the data I read into a single byte array, which is a problem for me.
The simplest approach IMO is to use Guava and its ByteStreams class:
byte[] bytes = ByteStreams.toByteArray(in);
Or for a file:
byte[] bytes = Files.toByteArray(file);
Alternatively (if you didn't want to use Guava), you could create a ByteArrayOutputStream, and repeatedly read into a byte array and write into the ByteArrayOutputStream (letting that handle resizing), then call ByteArrayOutputStream.toByteArray().
Note that this approach works whether you can tell the length of your input or not - assuming you have enough memory, of course.
Please keep in mind that the answers here assume that the length of the file is less than or equal to Integer.MAX_VALUE(2147483647).
If you are reading in from a file, you can do something like this:
File file = new File("myFile");
byte[] fileData = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(fileData);
dis.close();
UPDATE (May 31, 2014):
Java 7 adds some new features in the java.nio.file package that can be used to make this example a few lines shorter. See the readAllBytes() method in the java.nio.file.Files class. Here is a short example:
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
// ...
Path p = FileSystems.getDefault().getPath("", "myFile");
byte [] fileData = Files.readAllBytes(p);
Android has support for this starting in Api level 26 (8.0.0, Oreo).
You can use Apache commons-io for this task:
Refer to this method:
public static byte[] readFileToByteArray(File file) throws IOException
Update:
Java 7 way:
byte[] bytes = Files.readAllBytes(Paths.get(filename));
and if it is a text file and you want to convert it to String (change encoding as needed):
StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString()
You can read it by chunks (byte buffer[] = new byte[2048]) and write the chunks to a ByteArrayOutputStream. From the ByteArrayOutputStream you can retrieve the contents as a byte[], without needing to determine its size beforehand.
I believe buffer length needs to be specified, as memory is finite and you may run out of it
Example:
InputStream in = new FileInputStream(strFileName);
long length = fileFileName.length();
if (length > Integer.MAX_VALUE) {
throw new IOException("File is too large!");
}
byte[] bytes = new byte[(int) length];
int offset = 0;
int numRead = 0;
while (offset < bytes.length && (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + fileFileName.getName());
}
in.close();
Max value for array index is Integer.MAX_INT - it's around 2Gb (2^31 / 2 147 483 647).
Your input stream can be bigger than 2Gb, so you have to process data in chunks, sorry.
InputStream is;
final byte[] buffer = new byte[512 * 1024 * 1024]; // 512Mb
while(true) {
final int read = is.read(buffer);
if ( read < 0 ) {
break;
}
// do processing
}

Categories

Resources