0

These are two SpringBoot projects. A is for api service providing, and B is consuming A's service via rest template. It is OK while transferring string. While transferring Excel file via byte array, B receives a string, not byte array.

The api method in A.

@GetMapping(value = "/export")
@ResponseBody
public HSResult exportFile(@RequestParam(value = "fileName", defaultValue = "-1") String fileName,
                           @RequestParam(value = "provider", defaultValue = "-1") String channelId,
                           @RequestParam(value = "fileStatus", defaultValue = "-5") int fileStatus,
                           @RequestParam(value = "cdate", defaultValue = "") String cdate,
                           HttpServletRequest request, HttpServletResponse response) {

  // ...... some logic code
  InputStream inputStream=new FileInputStream(fullPath);
  long fileSize=new File(fullPath).length();
  byte[] allBytes = new byte[(int) fileSize];
  inputStream.read(allBytes);
  result = HSResult.build(200, "exportFile success",allBytes);
  return result;
}

The consuming method in B.

public ResponseEntity downLoadFile(int businessType, String fileName, int fileStatus, HttpServletResponse response) {

    //.......

    ResponseEntity<HSResult> responseEntity = restTemplate.getForEntity(url, HSResult.class);
    HSResult apiResult = responseEntity.getBody();
    byte[] fileData = (byte[]) apiResult.getData();

    //.......

}

A reads an excel file from disk into a byte array before transferring.

enter image description here

But while receving the result in B side, it is string like UEsDBC0AAAAIADhMuVAKVjbC//////////8LABQAX3Jl

enter image description here

Why did this happen? And how to transfer byte array through rest template correctly? Thanks.

PS: The class of HSResult

public class HSResult {

  private Integer status;
  private String msg;
  private Object data;

  //gets and setters    
  //......

}

Robin Sun
  • 1,352
  • 3
  • 20
  • 45
  • Probably because that's the type chosen by `HSResult` to represent the data. Have you tried turning the `String` back into bytes to see if the data is as expected? – Vince May 25 '20 at 04:40
  • The content field of ``HSResult`` is in type object. I tried turning back the String into bytes array and save the array in disk as Excel file. The file corrupted. – Robin Sun May 25 '20 at 05:06
  • Because HTTP is a text protocol you can't transfer raw binary data over it. You have to encode binary to text using base64 encoding on sender side. The receiver has to decode to binary again. – Ken Bekov May 26 '20 at 05:13
  • @KenBekov. Thanks Ken. By the way, if I change the data type in HSResult, everything is OK. Please see the answer below. Do you think that rest template automatically encode the byte array in the sender, and decode it in the receiver? – Robin Sun May 26 '20 at 06:54
  • @Robin Sun. I gues so, it does. But obviously converter needs to know that field's type is `byte[]` in order to apply conversion. – Ken Bekov May 26 '20 at 08:03

2 Answers2

2

Finally, I find the root cause. Share it with who may encounter the same issue.

  1. In A side, it puts a byte array in the data field of HSResult, this field is in type object.
  2. While receving this in B side, rest template is trying to cast all the data back into HSResult. When it comes to the byte array, the receving field data is in type object. So rest template does not konw what the specific type it is, then convert the byte array into a string, with some decode. I don't know whether it is exactly UTF8 or GB2312 or else.

How to resolve this? Just specify the receving field with specific type, not object.

public class HSResult {

    private Integer status;
    private String msg;
    private byte[] data;

    //gets and setters  

}

Then everythiing is OK.

Thanks for Ken's reminder. Http is a text protocal, it cannot transfer byte array directly, so byte array must be converted into string at first.

Robin Sun
  • 1,352
  • 3
  • 20
  • 45
0

I have used the Gson() class to convert object to json! and it has no problem with byte arrays.

I have this problem when I want to move my codes to spring, so I solved it by serializing byte[] filed:

class ByteArraySerializer: JsonSerializer<ByteArray>() {

    override fun serialize(bytes: ByteArray, jsonGenerator: JsonGenerator, serializerProvider: SerializerProvider?) {
        val intArray = intArray(bytes)
        jsonGenerator.writeArray(intArray, 0, intArray.size)
    }

    private fun intArray(input: ByteArray): IntArray {
        val ret = IntArray(input.size)
        for (i in input.indices) ret[i] = input[i].toInt() // & 0xff -> 0-255
        return ret
    }
}

convert byte array to int array

and then use it in my class:

data class VideoInfo(val durationMS: Int = 0): Serializable {

    @JsonSerialize(using = ByteArraySerializer::class)
    var bytes: ByteArray? = null
}

It will return json object as a below:

{
  "bytes": [-1,-40, ..., 0],
  "durationMS": 8870
}

It is kotlin, You can easily convert it to java :)

Khalil
  • 61
  • 3