NikosDim
NikosDim

Reputation: 1508

CXF "No Message body reader found" for byte array parameter in POST service

I am trying to write a service which will be responsible for uploading a file by taking the file as an array of bytes in the POST Entity. Here is my code

My CXF Service

@Path("MyTest")
public class TestService {
    @POST
    public String MyPost(Byte[] bytes){
        System.out.println("Service invoked");
        return "Hello, I am a POST response";
    }
}

My Client

File image = new File("C:\\snake.jpg");
FileInputStream is = new FileInputStream(image);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] fileInBytes = bos.toByteArray();

Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/MyApp");
target = target.path("MyTest");
Response response = target.request().post(Entity.entity(fileInBytes, MediaType.APPLICATION_OCTET_STREAM));

InputStream i = (InputStream) response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(i));
System.out.println(br.readLine());

And this is the error that I get

SEVERE: No message body reader has been found for class [Ljava.lang.Byte;, ContentType: application/octet-stream
Nov 06, 2014 4:02:50 PM org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: javax.ws.rs.WebApplicationException: HTTP 415 Unsupported Media Type
    at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1298)
...

Any ideas about it? Is there any better way to do a file uploading service?

Thanks

Upvotes: 2

Views: 7080

Answers (2)

user4820234
user4820234

Reputation: 11

Had exactly the same problem. Adding application/octet-stream as parameter to the @Consumes annotation solved the problem:

@POST
@Consumes({"application/octet-stream", "text/xml", "application/xml"})
@Produces({"text/xml", "application/xml"})
public Object post(String xmlData) throws Exception {
    ...
}

However, if you can ensure that the caller sets the appropriate Content-Type in the HTTP request header, it may not be necessary to add "application/octet-stream" to the @Consumes annotation.

An example using CURL:

curl -vX POST -H "Content-Type:text/xml" -T "postdata.xml" "http://localhost:9000/ws/rest/..."

Upvotes: 1

Paul Samsotha
Paul Samsotha

Reputation: 208944

You can use InputStream

@POST
public String MyPost(InputStream is) throws IOException  {
    BufferedImage image = ImageIO.read(is);
    JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(image)));
    System.out.println("Service invoked");
    return "Hello, I am a POST response";
}

Aside, in your client code, you haven't actually written anything to the output stream. It should be something more more like

FileInputStream is = new FileInputStream(image);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
    buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] fileInBytes = buffer.toByteArray();

Response response = target.request().post(Entity.entity(fileInBytes, 
        MediaType.APPLICATION_OCTET_STREAM));

UPDATE

You can actually use a byte array. You just need to use the primitive byte[]

@POST
public String MyPost(byte[] bytes) throws IOException  {
    JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bytes)));
    System.out.println("Service invoked");
    return "Hello, I am a POST response";
}

It also even possible to use File

@POST
public String MyPost(File file) throws IOException  {
    BufferedImage image = ImageIO.read(file);
    JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(image)));
    System.out.println("Service invoked");
    return "Hello, I am a POST response";
}

JAX-RS creates a temporary file for input on disk. It reads from the network buffer and saves the bytes read into this temporary file.

They all work. Pick your poison

Upvotes: 1

Related Questions