Balley 및 HttpEntity를 사용하지 않는 POST 멀티파트 요구 작업
이것은 그다지 질문이 아닙니다만, 필요에 따라서 제 작업 코드의 일부를 여기서 공유하고 싶습니다.
우리가 알고 있듯이HttpEntity
는 API22에서 폐지되어 API23 이후 완전히 삭제됩니다.현재 Android Developer에서 HttpEntity Reference에 액세스할 수 없습니다(404).다음은 Balley와 HttpEntity를 사용하지 않는 POST Multipart Request의 작업 샘플 코드입니다.동작하고 있습니다.Asp.Net Web API
물론 코드는 기존의 2개의 그리기 가능한 파일을 투고하는 기본적인 샘플일 뿐이며, 모든 경우에 최적인 솔루션은 아닙니다.또한 튜닝도 잘 되지 않습니다.
MultipartActivity.java:
package com.example.multipartvolley;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class MultipartActivity extends Activity {
private final Context context = this;
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private final String mimeType = "multipart/form-data;boundary=" + boundary;
private byte[] multipartBody;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multipart);
byte[] fileData1 = getFileDataFromDrawable(context, R.drawable.ic_action_android);
byte[] fileData2 = getFileDataFromDrawable(context, R.drawable.ic_action_book);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
// the first file
buildPart(dos, fileData1, "ic_action_android.png");
// the second file
buildPart(dos, fileData2, "ic_action_book.png");
// send multipart form data necesssary after file data
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// pass to multipart body
multipartBody = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
String url = "http://192.168.1.100/api/postfile";
MultipartRequest multipartRequest = new MultipartRequest(url, null, mimeType, multipartBody, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
Toast.makeText(context, "Upload successfully!", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Upload failed!\r\n" + error.toString(), Toast.LENGTH_SHORT).show();
}
});
VolleySingleton.getInstance(context).addToRequestQueue(multipartRequest);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_multipart, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private void buildPart(DataOutputStream dataOutputStream, byte[] fileData, String fileName) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\"; filename=\""
+ fileName + "\"" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(fileData);
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024 * 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dataOutputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
dataOutputStream.writeBytes(lineEnd);
}
private byte[] getFileDataFromDrawable(Context context, int id) {
Drawable drawable = ContextCompat.getDrawable(context, id);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
}
MultipartRequest.java:
package com.example.multipartvolley;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import java.util.Map;
class MultipartRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders;
private final String mMimeType;
private final byte[] mMultipartBody;
public MultipartRequest(String url, Map<String, String> headers, String mimeType, byte[] multipartBody, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
this.mMimeType = mimeType;
this.mMultipartBody = multipartBody;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return (mHeaders != null) ? mHeaders : super.getHeaders();
}
@Override
public String getBodyContentType() {
return mMimeType;
}
@Override
public byte[] getBody() throws AuthFailureError {
return mMultipartBody;
}
@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
@Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
갱신:
텍스트 부분은 아래 @Oscar의 답변을 참조하십시오.
@RacZo와 @BNK의 코드를 모듈러형으로 재작성하여 다음과 같이 사용하기 쉽게 합니다.
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
// parse success output
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("api_token", "gh659gjhvdyudo973823tt9gvjf7i6ric75r76");
params.put("name", "Angga");
params.put("location", "Indonesia");
params.put("about", "UI/UX Designer");
params.put("contact", "angga@email.com");
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
params.put("cover", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
코드가 가득 찬 체크VolleyMultipartRequest
내 요지에서는
답을 더하고 싶을 뿐이에요.텍스트 필드를 본문에 추가하는 방법을 알아보려고 하다가 다음 기능을 만들었습니다.
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
꽤 잘 되고 있어요.
utf-8 파라미터를 전송하는데 어려움을 겪고 있는 분들을 위해 dataOutputStream에서 문제가 발생하여 @RacZo의 코드를 아래 코드로 변경하였습니다.
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"");
dataOutputStream.write(parameterName.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.write(parameterValue.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
}
멀티 파트 요청에 통합하기 쉬운 오리지널 발리 라이브러리의 래퍼를 찾았습니다.또한 다른 요청 매개 변수와 함께 다중 부품 데이터 업로드도 지원합니다.그 때문에, 장래의 개발자에게 코드의 공유를 실시하고 있습니다(다른 파라미터와 함께 발리를 사용해 멀티 파트 데이터를 업로드 하는 등).
에 다음 라이브러리를 추가합니다.build.gradle
파일.
dependencies {
compile 'dev.dworks.libs:volleyplus:+'
}
원래의 balley 라이브러리를 에서 삭제하고 대신 유사한 통합 기술을 가진 멀티 파트와 일반 요청을 모두 처리할 수 있는 위의 라이브러리를 사용했습니다.
그리고 POST 요청 조작을 담당하는 다음 클래스를 작성해야 했습니다.
public class POSTMediasTask {
public void uploadMedia(final Context context, String filePath) {
String url = getUrlForPOSTMedia(); // This is a dummy function which returns the POST url for you
SimpleMultiPartRequest multiPartRequestWithParams = new SimpleMultiPartRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("Response", response);
// TODO: Do something on success
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle your error here
}
});
// Add the file here
multiPartRequestWithParams.addFile("file", filePath);
// Add the params here
multiPartRequestWithParams.addStringParam("param1", "SomeParamValue1");
multiPartRequestWithParams.addStringParam("param2", "SomeParamValue2");
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(multiPartRequestWithParams);
}
}
이제 다음과 같이 작업을 수행합니다.
new POSTMediasTask().uploadMedia(context, mediaPath);
이 라이브러리를 사용하여 한 번에 하나의 파일을 업로드할 수 있습니다.그러나 여러 작업을 시작하는 것만으로 여러 파일을 업로드할 수 있었습니다.
도움이 됐으면 좋겠네요!
다음은 Balley 1.1.1에서 멀티파트 요청을 허용하는 Kotlin 버전의 클래스입니다.
대부분 @BNK의 솔루션을 기반으로 하고 있지만, 상당히 심플합니다.특별한 퍼포먼스 문제는 없었습니다.5Mb 사진을 3초 만에 올렸어요.
class MultipartWebservice(context: Context) {
private var queue: RequestQueue? = null
private val boundary = "apiclient-" + System.currentTimeMillis()
private val mimeType = "multipart/form-data;boundary=$boundary"
init {
queue = Volley.newRequestQueue(context)
}
fun sendMultipartRequest(
method: Int,
url: String,
fileData: ByteArray,
fileName: String,
listener: Response.Listener<NetworkResponse>,
errorListener: Response.ErrorListener
) {
// Create multi part byte array
val bos = ByteArrayOutputStream()
val dos = DataOutputStream(bos)
buildMultipartContent(dos, fileData, fileName)
val multipartBody = bos.toByteArray()
// Request header, if needed
val headers = HashMap<String, String>()
headers["API-TOKEN"] = "458e126682d577c97d225bbd73a75b5989f65e977b6d8d4b2267537019ad9d20"
val request = MultipartRequest(
method,
url,
errorListener,
listener,
headers,
mimeType,
multipartBody
)
queue?.add(request)
}
@Throws(IOException::class)
private fun buildMultipartContent(dos: DataOutputStream, fileData: ByteArray, fileName: String) {
val twoHyphens = "--"
val lineEnd = "\r\n"
dos.writeBytes(twoHyphens + boundary + lineEnd)
dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"$lineEnd")
dos.writeBytes(lineEnd)
dos.write(fileData)
dos.writeBytes(lineEnd)
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd)
}
class MultipartRequest(
method: Int,
url: String,
errorListener: Response.ErrorListener?,
private var listener: Response.Listener<NetworkResponse>,
private var headers: MutableMap<String, String>,
private var mimeType: String,
private var multipartBody: ByteArray
) : Request<NetworkResponse>(method, url, errorListener) {
override fun getHeaders(): MutableMap<String, String> {
return if (headers.isEmpty()) super.getHeaders() else headers
}
override fun getBodyContentType(): String {
return mimeType
}
override fun getBody(): ByteArray {
return multipartBody
}
override fun parseNetworkResponse(response: NetworkResponse?): Response<NetworkResponse> {
return try {
Response.success(response, HttpHeaderParser.parseCacheHeaders(response))
} catch (e: Exception) {
Response.error(ParseError(e))
}
}
override fun deliverResponse(response: NetworkResponse?) {
listener.onResponse(response)
}
}
}
멀티파트 발리 POST 요청을 위한 매우 간단한 Kotlin 버전입니다.
private fun saveProfileAccount(concern: String, selectedDate: String, WDH: String, details: String, fileExtention: String) {
val multipartRequest: VolleyMultipartRequest =
object : VolleyMultipartRequest(
Request.Method.POST, APIURLS.WhistleBlower,
Response.Listener<JSONObject> { response ->
},
Response.ErrorListener { error ->
error.printStackTrace()
}) {
@Throws(AuthFailureError::class)
override fun getHeaders(): MutableMap<String, String>{
val params: MutableMap<String, String> = HashMap()
params[ConstantValues.XAppApiKey] = APIURLS.XAppApiValue
return params
}
override fun getParams(): Map<String, String>? {
val params: MutableMap<String, String> = HashMap()
val sharedPreferences: SharedPreferences = requireActivity().getSharedPreferences(Prefs.PREF_NAME, 0)
val userId = Prefs.getStringPref(sharedPreferences, Prefs.USER_ID)
val userName = Prefs.getStringPref(sharedPreferences, Prefs.FULL_NAME)
params["PersonId"] = userId.toString()
params["PersonName"] = userName.toString()
params["Concern"] = concern
params["DateOfHappen"] = selectedDate
params["WhereHappened"] = WDH
params["Ext"] = fileExtention
return params
}
override fun getByteData(): Map<String, DataPart>? {
val params: MutableMap<String, DataPart> = HashMap()
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params["cover"] = DataPart(
"sd.pdf",
byteArray,
"doc/pdf"
)
return params
}
}
AppSingleton.getInstance(requireContext())?.addToRequestQueue(multipartRequest)
}
언급URL : https://stackoverflow.com/questions/32240177/working-post-multipart-request-with-volley-and-without-httpentity
'source' 카테고리의 다른 글
Google Finance API를 사용하여 주식 시세를 얻으려면 어떻게 해야 합니다. (0) | 2022.11.19 |
---|---|
php가 비어있을 때 null입니까? (0) | 2022.11.19 |
Laravel 5.1에서 Laravel 5.8로 업그레이드한 후 whereHas()가 느려짐 (0) | 2022.11.19 |
날짜 문자열이 해당 형식의 유효한 날짜인지 확인 (0) | 2022.11.19 |
풀 URL 사용 시 PHP file_get_contents가 매우 느리다 (0) | 2022.11.19 |