开发者

HttpClient and MultipartEntity vs. Jersey Multipart and Android

开发者 https://www.devze.com 2023-01-23 01:41 出处:网络
I have a rest webservice that takes a POST metod with multipart message: @Path(\"transferFile\") @POST

I have a rest webservice that takes a POST metod with multipart message:

@Path("transferFile")     
 @POST  
@Consumes(MediaType.MULTIPART_FORM_DATA)  
@Produces(Media开发者_C百科Type.APPLICATION_XML)  
public String multipartTest(com.sun.jersey.multipart.MultiPart data) {  
try {  
// get first body part (index 0)          
BodyPart bp = multiPart.getBodyParts().get(0);  
etc..  

Now I am trying to write a java client for that. I started with a simple jersey client: view plaincopy to clipboardprint?

    MultiPart multiPart = new MultiPart();  
    multiPart.bodyPart( new BodyPart(wavestream,MediaType.APPLICATION_OCTET_STREAM_TYPE));  

    Client c = Client.create();  
    WebResource r = c.resource("http://127.0.0.1:8080/webapp:);
response=r.path("transferFile").type(MediaType.MULTIPART_FORM_DATA).accept(MediaType.APPLICATION_XML).post(String.class, multiPart);  

This works great - everything is ok. However I need this client working on Android and I have trouble with using jersey on that platform. So I used the normal way to send multipart message on android:

    HttpClient client = new DefaultHttpClient();
 client.getParams().setParameter("http.socket.timeout", new Integer(90000)); // 90 second 

HttpPost httpPost = new HttpPost("http://127.0.0.1:8080/webapp/transferFile");
 httpPost.setHeader("Content-Type", MediaType.MULTIPART_FORM_DATA );

//tried with and without base64
 byte [] encodedWavestream = Base64.encodeBytesToBytes(wavestream);
 InputStream ins = new ByteArrayInputStream(encodedWavestream);
 InputStreamBody body = new InputStreamBody(ins, "test" );
 int send = ins.available(); 

MultipartEntity requestContent = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE ); 
requestContent.addPart("stream", body);

httpPost.setEntity(requestContent); 
HttpResponse Response = client.execute(httpPost);

An this gives an annoying response from the server :

HTTP Status 400 - Bad Request  
The request sent by the client was syntactically incorrect (Bad Request).   

I check the server log files but there is nothing there. So I don't know what's the origin of this error. I have wrote a simple html page with a post formula and 'multipart/form-data' content-type and it also works! An auto-generated request from soapUI also works! Why my client does not work? Can anybody help?


There is bug in Jersey. See Chunked encoding problem.

This problem appears only for few clients (iOS, Android).

If you set the Content-Type to application/octet-stream, then the Jersey MessageWriter for the application/octet-stream will set the Content-Length and not send as chunked transport method.

There is solution for Jersey Client:

ClientConfig config = new DefaultClientConfig();
config.getProperties().put(ClientConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 32 * 1024);

But it doesn't work for the iOS's or Android's client. So I tested Apache File Upload. Threre was another bug: "Stream ended unexpectedly".

Only Oreilly upload can upload file correct for all clients. This is my code:

public Object[] getParametersAndFiles(HttpServletRequest request) throws IOException {
    log.debug("OreillyUpload");
    Properties params = new Properties();
    LinkedHashMap files = new LinkedHashMap();

    File tempDirectory = new File(System.getProperty("java.io.tmpdir")); 

    MultipartParser mp = new MultipartParser(request, 1*1024*1024); // 10MB
    Part part;
    while ((part = mp.readNextPart()) != null) {
        String name = part.getName();
        if (part.isParam()) {
            // it's a parameter part
            ParamPart paramPart = (ParamPart) part;
            String value = paramPart.getStringValue();
            params.put(name, value);

            log.debug("param; name=" + name + ", value=" + value);
        }
        else if (part.isFile()) {
            // it's a file part
            FilePart filePart = (FilePart) part;
            String fileName = filePart.getFileName();
            if (fileName != null) {
                // the part actually contained a file
                File file = new File(tempDirectory,fileName);
                long size = filePart.writeTo(file);
                files.put(name, file);

                log.debug("file; name=" + name + "; filename=" + fileName +
                        ", filePath=" + filePart.getFilePath() +
                        ", content type=" + filePart.getContentType() +
                        ", size=" + size);

            }
            else {
                // the field did not contain a file
                log.debug("file; name=" + name + "; EMPTY");
            }
        }
    }

    return new Object[] {params, files};
}

And this is Jersey Server code (warning all Jersey Upload anotations (like as "@FormDataParam") should be removed):

@POST
@Path("uploadMarkup")
@Produces(MediaType.APPLICATION_JSON)
//    @Consumes(MediaType.MULTIPART_FORM_DATA)
////    public void uploadMarkup(
//    public JSONWithPadding uploadMarkup(
//            @FormDataParam("markupFile") InputStream markupFile,
//            @FormDataParam("markupFile") FormDataContentDisposition details,
//            @FormDataParam("slideNum") int slideNum) {
public JSONWithPadding uploadMarkup(@Context HttpServletRequest request) {
    Object[] data = uploadService.getParametersAndFiles(request);
    ...
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号