2016-03-23 13 views
2

Mit einer Mischung aus dem Code in verwandten Antworten, gelang es mir, mit multipart zu telefonieren. Ich kann es jedoch nicht mit dem korrekten Parameternamen senden.Android Volley mehrteiliger Parametername

Wie muss der Antrag (von der iOS-App genommen) werden:

enter image description here

enter image description here

Wie meine Anfrage aussieht:

enter image description here

enter image description here

Code:

MultipartRequest soll eine mehrteilige Basisanforderung sein.

import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Matrix; 
import android.graphics.RectF; 
import android.util.Log; 

import com.android.volley.Request; 
import com.android.volley.Response; 

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.DataOutputStream; 
import java.io.File; 

import com.android.volley.AuthFailureError; 
import com.android.volley.toolbox.StringRequest; 

import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 

public class MultipartRequest extends StringRequest { 
private final int maxImageWidth = 200; 
private final int maxImageHeight = 200; 

static String mimeType; 
private final File file; 
DataOutputStream dos = null; 
String lineEnd = "\r\n"; 
static String boundary = "apiclient-" + System.currentTimeMillis(); 
String twoHyphens = "--"; 
int bytesRead, bytesAvailable, bufferSize; 
byte[] buffer; 
int maxBufferSize = 124 * 124; 

public static MultipartRequest newInstance(final PlayEarnAPIImpl.OnPlayEarnAPIResponse listener, File file, Response.Listener<String> responseListener, Response.ErrorListener errorListener, String serviceURL) { 
    mimeType = "multipart/form-data;boundary=" + boundary; 
    return new MultipartRequest(Request.Method.PUT, serviceURL, responseListener, errorListener, file, serviceURL); 
} 

public MultipartRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener, File file, String serviceURL) { 
    super(method, url, listener, errorListener); 
    this.file = file; 
} 

private byte[] decodeFile(File file) { 
    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    options.inSampleSize = calculateInSampleSize(options, 400, 400); 
    Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); 
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); 
    return byteArrayOutputStream.toByteArray(); 
} 

public static int calculateInSampleSize(
     BitmapFactory.Options options, int reqWidth, int reqHeight) { 
    // Raw height and width of image 
    final int height = options.outHeight; 
    final int width = options.outWidth; 
    int inSampleSize = 1; 

    if (height > reqHeight || width > reqWidth) { 

     final int halfHeight = height/2; 
     final int halfWidth = width/2; 

     // Calculate the largest inSampleSize value that is a power of 2 and keeps both 
     // height and width larger than the requested height and width. 
     while ((halfHeight/inSampleSize) > reqHeight 
       && (halfWidth/inSampleSize) > reqWidth) { 
      inSampleSize *= 2; 
     } 
    } 

    return inSampleSize; 
} 

//solution 2 
private byte[] shrinkImage(File file) { 
    try { 
     int inWidth = 0; 
     int inHeight = 0; 

     InputStream in = new FileInputStream(file.getAbsolutePath()); 

     // decode image size (decode metadata only, not the whole image) 
     BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inJustDecodeBounds = true; 
     BitmapFactory.decodeStream(in, null, options); 
     in.close(); 
     in = null; 

     // save width and height 
     inWidth = options.outWidth; 
     inHeight = options.outHeight; 

     // decode full image pre-resized 
     in = new FileInputStream(file.getAbsolutePath()); 
     options = new BitmapFactory.Options(); 
     // calc rought re-size (this is no exact resize) 
     options.inSampleSize = Math.max(inWidth/maxImageWidth, inHeight/maxImageHeight); 
     // decode full image 
     Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options); 

     // calc exact destination size 
     Matrix m = new Matrix(); 
     RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); 
     RectF outRect = new RectF(0, 0, maxImageWidth, maxImageHeight); 
     m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); 
     float[] values = new float[9]; 
     m.getValues(values); 

     // resize bitmap 
     Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); 

     // save image 

     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
     resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); 
     return byteArrayOutputStream.toByteArray(); 
    } catch (IOException e) { 
     Log.e("Image", e.getMessage(), e); 
    } 
    return null; 
} 

@Override 
public String getBodyContentType() { 
    return mimeType; 
} 

@Override 
public byte[] getBody() throws AuthFailureError { 
    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    dos = new DataOutputStream(bos); 
    byte[] bitmapData = null; 
    try { 
     //dos.writeBytes(twoHyphens + boundary + lineEnd); 
     dos.writeBytes(lineEnd); 
     dos.writeBytes(" ------------------12345"); 
     dos.writeBytes(lineEnd); 
     dos.writeBytes("Content-Disposition: form-data; name=\"picture\"; filename=\"file.png\""); 
     dos.writeBytes(lineEnd); 
     dos.writeBytes("Content-Type: image/png"); 
     //dos.writeBytes("Content-Disposition: form-data; name=\"picture\";filename=\"" 
     //  + file.getName() + "\"" + lineEnd); 
     dos.writeBytes(lineEnd); 
     bitmapData = shrinkImage(this.file); 
     ByteArrayInputStream fileInputStream = new ByteArrayInputStream(bitmapData); 
     bytesAvailable = fileInputStream.available(); 

     bufferSize = Math.min(bytesAvailable, maxBufferSize); 
     Log.d("MultipartRequest", "bufferSize:" + bufferSize); 
     buffer = new byte[bufferSize]; 

     // read file and write it into form... 
     bytesRead = fileInputStream.read(buffer, 0, bufferSize); 

     while (bytesRead > 0) { 
      dos.write(buffer, 0, bufferSize); 
      bytesAvailable = fileInputStream.available(); 
      bufferSize = Math.min(bytesAvailable, maxBufferSize); 
      bytesRead = fileInputStream.read(buffer, 0, bufferSize); 
     } 

     // send multipart form data necesssary after file data... 
     dos.writeBytes(lineEnd); 
     dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); 

     return bos.toByteArray(); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    return bitmapData; 
} 

Dies ist die Anfrage Ich verwende:

import com.android.volley.AuthFailureError; 
import com.android.volley.Request; 
import com.android.volley.Response; 
import com.android.volley.VolleyError; 
import com.google.gson.Gson; 

import java.io.File; 
import java.util.Map; 

public class ChangeProfileImageRequest extends MultipartRequest { 
private static final String UPDATE_PATH = "users/update"; 
String serviceURL = APIImpl.API_URL + UPDATE_PATH; 

public static ChangeProfileImageRequest newInstance(final APIImpl.OnAPIResponse listener, File file) { 
    String serviceURL = APIImpl.API_URL + UPDATE_PATH; 

    return new ChangeProfileImageRequest(Request.Method.PUT, serviceURL, new ResponseListener(listener), new ErrorListener(listener), file, serviceURL); 
} 

public ChangeProfileImageRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener, File file, String serviceURL) { 
    super(method, url, listener, errorListener, file, serviceURL); 
} 

@Override 
public Map<String, String> getHeaders() throws AuthFailureError { 
    Map<String, String> headers = new APIImpl().getTokenHeader(); 
    //headers.put("Accept", "*/*"); 
    headers.put("Content-Type", "multipart/form-data;boundary=----------------12345"); 
    AppController.getInstance().addSessionCookie(headers); 
    return headers; 
} 

Also ich denke, es hat etwas mit dem Screenshot zu tun, die „multi Körper kann nicht entschlüsseln“, sagt, aber ich weiß nicht, Was ist das spezifische Problem? Auch von der Serverseite wird der Bildparameter nicht empfangen.

+0

IMO, vielleicht 'dos.writeBytes (" ------------------ 12345 ");' jedoch verwenden Sie 'dos.writeBytes (twoHyphens + boundary + twoHyphens + lineEnd); 'verursacht später dieses Problem. Sie können ein Beispiel unter https://www.asp.net/web-api/overview/advanced/sending-html-form-data-part-2 sehen (achten Sie auf 'Das Format einer mehrteiligen MIME-Nachricht ist am einfachsten zu Verstehen Sie, indem Sie auf eine Beispielanfrage schauen '). Versuchen Sie meinen Code http://StackOverflow.com/Questions/32240177/working-post-multipart-request-with-volley-and-without-httpentity, um zu sehen, ob es helfen kann – BNK

Antwort

-1

Verwendung von Apache MultipartEntityBuilder, dann können Sie nur die HttpEntity verwenden Ihre getBodyContentType, um() und getBody() zB:

... 

    private HttpEntity mEntity; 

    @Override 
    public String getBodyContentType() { 
     return mEntity.getContentType().getValue(); 
    } 

    @Override 
    public byte[] getBody() throws AuthFailureError { 
     ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
     try { 
      mEntity.writeTo(outputStream); 
     } catch (IOException e) { 
      VolleyLog.e("IOException @ " + getClass().getSimpleName()); 
     } 
     return outputStream.toByteArray(); 
    } 

    public void setEntity(HttpEntity entity) { 
     this.mEntity = entity; 
    } 
} 
+0

Hallo @Submersed, danke für die Antwort. Soweit ich weiß, ist org.apache.http. * Veraltet und sollte nicht verwendet werden. – RominaV

+0

@David Hackro Es unterstützt immer noch die gleiche Formatierung für eine mehrteilige Anfrage, also macht -1 für etwas, das er bereits verwendet, nicht wirklich viel Sinn. Sehen Sie sich zuerst die tatsächlichen Gründe für die Ablehnung an. In jedem Fall ist es meiner Meinung nach immer noch besser als die aktuelle Lösung. – Submersed

+0

Das ASOP entfernt sich von den Http-Bibliotheken zugunsten von UrlConnection, was nicht bedeutet, dass Apache selbst das gesamte Paket veraltet hat - markiert die Dokumentation die gesamte Klasse als veraltet? Nee. – Submersed

0

ich es Arbeit machen verwaltet Hinzufügen com.squareup.okhttp Gradle und Ändern Sie this class nur ein bisschen.

Basismehrteiliger Anfrage:

import com.android.volley.AuthFailureError; 
import com.android.volley.DefaultRetryPolicy; 
import com.android.volley.NetworkResponse; 
import com.android.volley.Request; 
import com.android.volley.Response; 
import com.android.volley.RetryPolicy; 
import com.android.volley.VolleyLog; 
import com.android.volley.toolbox.HttpHeaderParser; 

import com.squareup.okhttp.Headers; 
import com.squareup.okhttp.MediaType; 
import com.squareup.okhttp.MultipartBuilder; 
import com.squareup.okhttp.RequestBody; 

import android.util.Log; 

import java.io.File; 
import java.io.IOException; 
import java.io.UnsupportedEncodingException; 
import java.util.HashMap; 
import java.util.Map; 

import okio.Buffer; 

/** 
* Multipart request for Google's Volley using Square's OkHttp. 
* 
* @author Hussain Al-Derry 
* @version 1.0 
*/ 
public class VolleyMultipartRequest extends Request<String> { 

    /* Used for debugging */ 
    private static final String TAG = VolleyMultipartRequest.class.getSimpleName(); 

    /* MediaTypes */ 
    public static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg"); 
    public static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); 
    public static final MediaType MEDIA_TYPE_TEXT_PLAIN = MediaType.parse("text/plain"); 

    private MultipartBuilder mBuilder = new MultipartBuilder(); 
    private final Response.Listener<String> mListener; 
    private RequestBody mRequestBody; 

    public VolleyMultipartRequest(int method, String url, 
            Response.ErrorListener errorListener, 
            Response.Listener<String> listener) { 
     super(method, url, errorListener); 
     mListener = listener; 
     mBuilder.type(MultipartBuilder.FORM); 
    } 

    /** 
    * Adds a collection of string values to the request. 
    * 
    * @param mParams {@link HashMap} collection of values to be added to the request. 
    */ 
    public void addStringParams(HashMap<String, String> mParams) { 
     for (Map.Entry<String, String> entry : mParams.entrySet()) { 
      mBuilder.addPart(
        Headers.of("Content-Disposition", "form-data; name=\"" + entry.getKey() + "\""), 
        RequestBody.create(MEDIA_TYPE_TEXT_PLAIN, entry.getValue())); 
     } 
    } 

    /** 
    * Adds a single value to the request. 
    * 
    * @param key String - the field name. 
    * @param value String - the field's value. 
    */ 
    public void addStringParam(String key, String value) { 
     mBuilder.addPart(
       Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""), 
       RequestBody.create(MEDIA_TYPE_TEXT_PLAIN, value)); 
    } 

    /** 
    * Adds a binary attachment to the request. 
    * 
    * @param content_type {@link MediaType} - the type of the attachment. 
    * @param fileName  String - the attachment field name. 
    * @param value  {@link File} - the file to be attached. 
    */ 
    public void addAttachment(MediaType content_type, String paramName, String fileName, File value) { 
     mBuilder.addFormDataPart(paramName, fileName, RequestBody.create(content_type, value)); 
    } 

    /** 
    * Builds the request. 
    * Must be called before adding the request to the Volley request queue. 
    */ 
    public void buildRequest() { 
     mRequestBody = mBuilder.build(); 
    } 

    @Override 
    public String getBodyContentType() { 
     return mRequestBody.contentType().toString(); 
    } 

    @Override 
    public byte[] getBody() throws AuthFailureError { 
     Buffer buffer = new Buffer(); 
     try { 
      mRequestBody.writeTo(buffer); 
     } catch (IOException e) { 
      Log.e(TAG, e.toString()); 
      VolleyLog.e("IOException writing to ByteArrayOutputStream"); 
     } 
     return buffer.readByteArray(); 
    } 

    @Override 
    protected Response<String> parseNetworkResponse(NetworkResponse response) { 
     String parsed; 
     try { 
      parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 
     } catch (UnsupportedEncodingException e) { 
      parsed = new String(response.data); 
     } 
     return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); 
    } 

    @Override 
    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) { 
     return super.setRetryPolicy(retryPolicy); 
    } 

    @Override 
    protected void deliverResponse(String response) { 
     if (mListener != null) { 
      mListener.onResponse(response); 
     } 
    } 
} 

Und Subklassen es für meine spezifische Anfrage:

public class ChangeProfileImageRequest extends VolleyMultipartRequest { 
private static final String UPDATE_PATH = "users/update"; 

public static ChangeProfileImageRequest newInstance(final APIImpl.OnAPIResponse listener, File file) { 
    String serviceURL = APIImpl.API_URL + UPDATE_PATH; 

    //return new ChangeProfileImageRequest(Request.Method.PUT, serviceURL, new ResponseListener(listener), new ErrorListener(listener), file, serviceURL); 
    ChangeProfileImageRequest request = new ChangeProfileImageRequest(Method.PUT, serviceURL, new ErrorListener(listener), new ResponseListener(listener)); 
    request.addAttachment(MEDIA_TYPE_PNG, "picture", "file.png", file); 

    return request; 
} 

public ChangeProfileImageRequest(int method, String url, Response.ErrorListener errorListener, Response.Listener<String> listener) { 
    super(method, url, errorListener, listener); 
} 

@Override 
public Map<String, String> getHeaders() throws AuthFailureError { 
    Map<String, String> headers = new APIImpl().getTokenHeader(); 
    headers.put("Accept", "*/*"); 
    AppController.getInstance().addSessionCookie(headers); 
    return headers; 
} 

//ErrorListener and ResponseListener classes 

und Hinzufügen Volley:

public void updateProfileImage(APIImpl.OnAPIResponse listener, File image) { 
    ChangeProfileImageRequest request = ChangeProfileImageRequest.newInstance(listener, image); 
    request.buildRequest(); 
    // Access the RequestQueue through your singleton class. 
    request.setRetryPolicy(new DefaultRetryPolicy(
      0, 
      DefaultRetryPolicy.DEFAULT_MAX_RETRIES, 
      DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); 
    VolleySingleton.getInstance().addToRequestQueue(request); 
} 

ich als Antwort ein akzeptieren werde das verwendet jedoch nicht mehr als Volley. Ich denke, es ist nicht ideal, Volley AND OkHttp zu verwenden, nur weil eine Anfrage nicht funktioniert.

+0

Darf ich wissen, was ist APIImpl und AppController? –

+0

@GurupadMamadapur Ich denke nicht, dass es für die Frage relevant ist, aber 'APIImpl' ist nur die Implementierung einer Fassade, die ich machen musste, weil ich mit Dummy-Calls anfing, weil der Server nicht fertig war. Der 'AppController' ist eine Klasse, die von' MultiDexApplication' abgeleitet ist – RominaV