android volley posting binary body

2019-02-28 11:24发布

问题:

Scenario - upload binary data in the body of a post, handle a response body containing JSON.

How to do the following using Volley?

 curl -X  POST  -H "X-Application-Id: 3KxPB" -H "X-REST-API-Key: jkuI9"  -H "Content-Type: audio/3gp"  --data-binary '@test.3gp' https://host/1/files/audio

IMO - there is a gap in Volley handling binary POST body types that apache httpclient handles in subclasses of abstracthttpentity. If buffered binary data generated on the phone by camera, microphone, or other binary output sensors needs a mechanism to be wrapped and written to the body of a POST how to do it in volley?

I've looked at PoolingByteArrayOutputStream and would like to do something like fill the buffer and get the PBAOutStrm ,writing to PBAOutStrm from the buffer and then flipping OutStrm to InputStream and then wrap it in the body of a POST request as something like a ByteArrayEntity. I cant see how to do that in volley.

回答1:

I was able to solve this using a Volley GsonRequest:

public class MainActivity extends AppCompatActivity {

String url = "https://arcane-anchorage-34204.herokuapp.com/handleCode";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    JSONObject jsonBody = null;
    try {
        jsonBody = new JSONObject ("{\"code\":\"NZ4UBUB\"}");
    } catch (JSONException e) {
        Toast.makeText(getApplicationContext(), "Error e = " + e, Toast.LENGTH_SHORT).show();
    }

    Map<String, String> headers = new HashMap<String, String>();
    headers.put("Content-Type", "application/json");

    RequestQueue queue = Volley.newRequestQueue(this);

    GsonRequest<Routine[]> gsonRequest = new GsonRequest<Routine[]>(Request.Method.POST, url, Routine[].class, headers, new Response.Listener<Routine[]>() {
        @Override
        public void onResponse(Routine[] routineData) {
            TextView serverData = (TextView)findViewById(R.id.serverData);

            String complete = "";
            String repeat = "";
            String hold = "";
            String perform = "";
            String txtData = "";

            for (int i = 0; i < routineData.length; i++) {
                complete = (routineData[i].instructions.complete != null) ? "Complete: " + routineData[i].instructions.complete : "";
                repeat = (routineData[i].instructions.repeat != null) ? "Repeat: " + routineData[i].instructions.repeat : "";
                hold = (routineData[i].instructions.hold != null) ? "Hold: " + routineData[i].instructions.hold : "";
                perform = (routineData[i].instructions.perform != null) ? "Perform: " + routineData[i].instructions.perform : "";

                txtData += "DESCRIPTION: " + routineData[i].description[0] + ": " + routineData[i].description[1] + ", " + complete  + ", " + repeat  + ", " + hold  + ", " + perform + " ";
            }
            serverData.setText("Response: " + txtData);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            TextView serverData = (TextView)findViewById(R.id.serverData);

            serverData.setText("Response: " + volleyError.toString());

        }
    }, jsonBody);
    queue.add(gsonRequest);
}
public class GsonRequest<T> extends Request<T> {
    private final Gson gson = new Gson();
    private final Class<T> clazz;
    private final Map<String, String> headers;
    private final Response.Listener<T> listener;
    private JSONObject parameters = null;

    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     */
    public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
                       Response.Listener<T> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.clazz = clazz;
        this.headers = headers;
        this.listener = listener;
    }

    public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
                       Response.Listener<T> listener, Response.ErrorListener errorListener, JSONObject parameters) {
        this(method, url, clazz, headers, listener, errorListener);
        this.parameters = parameters;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    public String getBodyContentType() {
        return "application/json";
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        try {
            return parameters.toString().getBytes(getParamsEncoding());
        } catch (UnsupportedEncodingException e) {
        }
        return null;
    }

    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(
                    response.data, HttpHeaderParser.parseCharset(response.headers));
            Log.i("RESPONSE", json);
            return Response.success(
                    gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
  }
}


回答2:

To send binary data you can do something like what I did in this answer How to send a “multipart/form-data” POST in Android with Volley .