43

I'm reading a file into a byte array in chunks and sending it over the network via a POST request to a webserver. It's not anything complicated, I've done it before using this exact same code. This time, I noticed that my images are looking really odd when they get to the server, so I decided to look at the byte array being sent and the one being received just to make sure it was the same. It's not. On the java sending side the byte array contains negative numbers. On the C# receiving side, there are no negative numbers.

The first 15 bytes on the receiving side (C#)

137
80
78
71
13
10
26
10
0
0
0
13
73
72
68

Those same bytes but on the sending side (java)

-119
80
78
71
13
10
26
10
0
0
0
13
73
72
68

All of the non-negative numbers are the same, and the -119 isn't the only negative number, they are all over. I did notice that -119 and 137 are 256 apart and wondered if that has something to do with it.

The code I'm using to read the image (java)

public static byte[] readPart(String fileName, long offset, int length) throws FileNotFoundException, Exception
{
    byte[] data = new byte[length];
    File file = new File(fileName);
    InputStream is = new FileInputStream(file);
    is.skip(offset);
    is.read(data,0,data.length);
    is.close();
    return data;
}

The code I'm using to write the data (c#)

    private void writeFile(string fileName, Stream contents)
    {
        using (FileStream fs = new FileStream(fileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
        {
            int bufferLen = 65000;
            byte[] buffer = new byte[bufferLen];
            int count = 0;
            while ((count = contents.Read(buffer, 0, bufferLen)) > 0)
            {
                fs.Write(buffer, 0, count);
            }
            fs.Close();
        }
        contents.Close();
    }

I don't know if that is something that always happens and I just never noticed it before or if it is something that decided to go horribly wrong. What I do know is that this code worked before for something very similar and that it's not working now.

If anyone has any suggestions or an explanation I would really appreciate it.

EDIT: The reason my images were looking odd is how I was calling the readPart method.

byte[] data = FileUtilities.readPart(fileName,counter,maxFileSize);//counter is the current chunk number

How I should have been calling it

byte[] data = FileUtilities.readPart(fileName,counter*maxFileSize,maxFileSize);//the current chunk * cuhnksize for the offset...

Thanks everyone, I'm significantly less confused now :)

nick
  • 2,833
  • 3
  • 34
  • 61
  • Just tried making all the bytes positive before they were sent using data[i] = (byte)Math.abs((int)data[i]); That doesn't work, it tells me the image is corrupt at that point. – nick Mar 07 '12 at 21:35
  • Is it only the first byte of the whole file, or the first byte of every chunk? – Francois Mar 07 '12 at 21:37
  • 3
    Java's `byte` is signed, so it's not possible to have 137 stored in a `byte`. Maybe your problem comes from this? – Laf Mar 07 '12 at 21:37
  • Signed and unsigned bytes should have nothing to do with the images showing up oddly. Are you sure the entire image is sent over? Write the result image to file and compare the bytes. – CookieOfFortune Mar 07 '12 at 21:40
  • @CookieOfFortune More than the entire image is sent over... I just found that out. When my max chunk size is large enough to send the entire file, it's fine. When it's not, for example 1024, I send a total of 135168 bytes where I only want to send 134762. The 135168 is the file size being saved as well, so I suspect that is what is happening with the strange images – nick Mar 07 '12 at 21:59
  • Yeah, the semantics of read methods using that parameter pattern bite a lot of people. – Hot Licks Jun 24 '13 at 22:06

5 Answers5

48

In Java, byte is a signed value (using two's complement to encode negative values), so what you see it correct if unexpected by most people.

To convert a byte to an unsigned int value, use b & 0xff

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 2
    I can confirm it works, and kind of understand it. Also adding + 256 to that -25 (in my case 0xE7) actually works as well. Still, I don't *fully* grasp what's the actual problem here. Can you explain a bit more what the bit shifting does? – StuFF mc Oct 15 '14 at 12:42
  • 1
    `(byte)0xE7 + 256` converts the signed byte to `int` (which still gives you -25) and then adds `(int)256`. To fully understand what is going on, you must first understand that everything is just a bit pattern and *you* are giving those bits meaning by defining math operations in certain ways to encode real things. With that knowledge, you can read the linked article to understand what the bit pattern means. The next step is then to understand Java's type widening rules: http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html – Aaron Digulla Oct 16 '14 at 09:47
33

Java doesn't have unsigned bytes; all bytes are treated as signed. That's all.

All that really matters is how you think of the bytes, since you rarely ever actually need to do comparisons on bytes. The only significant difference is that they print out as signed, as you've discovered.

If you like, you can use e.g. Guava's UnsignedBytes utilities to view Java bytes as unsigned, but there's really not much practical difference.

Iulian Popescu
  • 2,595
  • 4
  • 23
  • 31
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 3
    And in C#, `byte` is unsigned and `sbyte` is signed. – Kirk Woll Mar 07 '12 at 21:36
  • 2
    From Java 1.8, the `Byte` class has gained some static methods to get the unsigned value of the byte: * one for int `Byte.toUnsignedInt(byte)`, * one for long `Byte.toUnsignedLong(byte)` – Arnaud JOLLY Feb 08 '19 at 10:57
14

As a further explanation, assume you have 137 as an unsigned byte. That is represented as:

1000 1001

This binary value, when expressed as a signed two's complement number, turns out to be -119. (-128 + 9)

Any unsigned byte values over 128 will be affected by the difference since the left-most bit is used in this way by the two's complement scheme.

Peter
  • 6,354
  • 1
  • 31
  • 29
  • Finally I was able to validate if the bytes are correct when debugging. I have -16, which in turn is 240 as ubyte (240 - 256) = - 16 – Afonso Lage Nov 26 '15 at 10:59
5

Maybe it has something to do with the fact that Java's byte is signed (range -128 to 127) while C#'s is unsigned (0 to 255) :). The information is the same in binary, it's just interpreted differently.

Milo
  • 431
  • 2
  • 4
  • 1
    for why Java has negative numbers is byte? what are they useful?? – chiperortiz Mar 05 '19 at 02:12
  • chiperortiz I assume it is just in order to match the convention of all its other primitive binary types. I wish they had enabled signed and unsigned types but here we are, I think that would be a major change at this point. You just have to be careful when dealing with "negative" values that might get automatically up-casted to larger types in Java because it will sign-extend them instead of prefixing with zeros, which can lead to bugs if you weren't expecting that. – theferrit32 Aug 06 '19 at 00:06
3

The range of byte is from -128 to 127, so if you try to assign a byte 128, it will cycle around and the result will be -128.

System.out.println("Max val = " + Byte.MAX_VALUE);   //prints: Max val = 127
System.out.println("Min val = " + Byte.MIN_VALUE);   //prints: Min val = -128

System.out.println("(byte)137 = " + (byte)137);      //prints: (byte)137 = -119
System.out.println("(byte)128 = " + (byte)128);      //prints: (byte)128 = -128
System.out.println("(byte)-129 = " + (byte)-129);    //prints: (byte)-129 = 127
PrecisionLex
  • 801
  • 11
  • 26