ভলির সাথে এবং এইচটিপিএনটিটি ছাড়াই পোষ্ট মাল্টিপার্ট অনুরোধ কাজ করছে


106

এটি সত্যিই কোনও প্রশ্ন নয়, তবে আপনার প্রয়োজনের সময় আমি এখানে আপনার রেফারেন্সের জন্য আমার কিছু কার্যকরী কোডটি ভাগ করতে চাই।

যেহেতু আমরা জানি যে HttpEntityএপিআই 22 থেকে অবমূল্যায়ন করা হয়েছে এবং API23 এর পরে পুরোপুরি সরানো হয়েছে। এই মুহুর্তে, আমরা আর অ্যান্ড্রয়েড বিকাশকারী (404) এ এইচটিপিএন্টিটি রেফারেন্স অ্যাক্সেস করতে পারি না । সুতরাং, ভলির সাথে এবং এইচটিপিএন্টিটি ছাড়াই পোষ্ট মাল্টিপার্ট অনুরোধের জন্য নিম্নলিখিতটি আমার কার্যকারী নমুনা কোড । এটি কাজ করছে, পরীক্ষিত Asp.Net Web API। অবশ্যই কোডটি কেবলমাত্র একটি প্রাথমিক নমুনা যা দুটি অস্তিত্বের আঁকতে সক্ষম ফাইলগুলি পোস্ট করে, এটিও সব ক্ষেত্রেই সেরা সমাধান নয়, এবং ভাল টিউনিং নয়।

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);
    }
}

হালনাগাদ:

পাঠ্য অংশের জন্য, দয়া করে নীচে @ অস্কারের উত্তরটি দেখুন।


1
আমি নীচের প্রশ্নটিতে @ কেভিন মন্তব্যটি অনুলিপি করেছি : কিছু সার্ভারগুলি খুব পিক y আপনার যদি সমস্যা থাকে তবে ";" এর মধ্যে একটি স্পেস যোগ করুন; এবং "ফাইলের নাম =" কন্টেন্ট-বিভাজন এবং "মাল্টিপার্ট / ফর্ম-ডেটা তৈরি করার সময়; সীমানা =" + সীমানা; :)
বিএনকে

1
আপনি যদি মিমি টাইপ যুক্ত করতে চান: ডেটাআউটপুটস্ট্রিম.উইরাইটবাইটস ("বিষয়বস্তুর ধরণ: চিত্র / jpeg" + লাইনএন্ড);
মওর হাদাদ

1
@ মওরহাদাদ: আপনার মন্তব্যের জন্য ধন্যবাদ :)
বিএনকে

1
এই দুর্দান্ত সমাধানের জন্য আপনাকে ধন্যবাদ। অ্যাপক্যামপ্যাট ২৩ এ আপডেট করার পরে এই ইস্যুটি **
মওর হাদাদ

1
প্রিয় বিএনকে ভিডিও আপলোড করার জন্য এটি কাজ করে?
মোক

উত্তর:


63

আমি আপনার কোড @ র্যাকজো এবং @ বিএনকে আরও মডিউলার এবং পছন্দ মতো সহজেই লিখতে চাই

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আমার সারাংশে কোড পূর্ণ কোড পরীক্ষা করুন ।


আমি বাইট বা স্ট্রিংয়ে রূপান্তর করতে চাই না। আমার ক্ষেত্রে, সার্ভার-সাইডটি কোনও ফাইল এবং একাধিক পাঠ্য (কী-মান জোড়া, মাল্টিপার্ট ফর্ম ডেটা) প্রত্যাশা করে, বাইট বা স্ট্রিং নয়। এটা কি সম্ভব?
মিলন

হ্যাঁ এটি, আপনি সার্ভারে আপনার ডেটা যেমন ওয়েব ফর্মের মাল্টিপার্ট ফর্ম ডেটার মতো চিকিত্সা করতে পারেন, আসলে কোডটি ফিট করার জন্য এইচটিপি শিরোনামের অনুরোধ এবং অনুরূপ ওয়েব ফর্ম তাই এটি আপনার সমাধান খুঁজছেন ...
অ্যাঙ্গা আরি উইজায়া

@ আহমাদউল্লাহসাইকেট আপনি কিছু পেয়েছেন কারণ আমার প্রকল্পের একই জিনিস একই নামের সাথে একাধিক অনুরোধ প্রেরণ করে
রিকি প্যাটেল

20

শুধু উত্তর যুক্ত করতে চান। আমি কীভাবে দেহে পাঠ্য ক্ষেত্রগুলি সংযোজন করব তা বোঝার চেষ্টা করছিলাম এবং এটি করার জন্য নিম্নলিখিত ফাংশনটি তৈরি করেছি:

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);
}

এটি বেশ ভাল কাজ করছে।


আমি আমার অ্যাপে @ বিএনকে সমাধান এম্বেড করেছি। আমি আমার ফোনের লাইব্রেরিতে একটি ফটো নির্বাচন করছি এবং এটি মাল্টিপার্ট ফর্ম ডেটার মাধ্যমে প্রেরণ করছি, তবে এটি সার্ভারে প্রেরণের আগে কয়েক সেকেন্ড (10-15 ডলার) লাগে। এই ওভারহেড হ্রাস করার কোনও উপায় আছে কি? বা অন্য কোন সুপারিশ?
ক্যাসিলা 16

1
এইচটিপিএনটিটির অবক্ষয়ের পরে, ভলির সাথে মাল্টি-পার্ট আপলোডগুলি খুব জটিল হয়ে ওঠে, সাথে সাথে প্যাচচ অনুরোধগুলি প্রয়োগ করা মাথা ব্যথার কারণ আমি ভলির থেকে দূরে সরে গিয়ে আমার সমস্ত অ্যাপ্লিকেশনগুলিতে রেট্রোফিট ( স্কোয়ার.github.io/retrofit ) প্রয়োগ করেছি । আমি আপনাকেও এটি করার পরামর্শ দিচ্ছি কারণ রেট্রোফিট আপনাকে আরও ভাল পশ্চাৎসঙ্গততা এবং ভবিষ্যতের প্রমাণগুলি প্রদান করে App
অস্কার সালগেরো

আমি @ র্যাকজোর সাথে একমত, যদিও আমি ওকে এইচটিটিপি পছন্দ করি :), আমি এখনও অন্য নেটওয়ার্কের অনুরোধের জন্য ভোলি ব্যবহার করি। আমি গুগলের ভলিকে অ্যাপাচি লাইব অপসারণ কাস্টমাইজ করেছি , github.com/ngocchung/volleynoapache এ পোস্ট করা হয়েছে , তবে কেবল গেট, পোস্ট এবং মাল্টিপার্টের জন্য পরীক্ষিত।
বিএনকে

ভলির লাইব্রেরি ব্যবহার করে আমার প্যাচ অনুরোধটি ব্যবহার করা দরকার। আমি কীভাবে এটিকে জাগাতে পারি?
জগদেশেশ সেরাম

8

যারা utf-8 পরামিতিগুলি প্রেরণে লড়াই করে যাচ্ছেন এবং এখনও ভাগ্য নেই, আমার যে সমস্যাটি ছিল তা ডেটা আউটপুট স্ট্রিমে ছিল এবং @ র্যাকজো এর কোডটি নীচের কোডে পরিবর্তন করেছেন:

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);
    } 

2

ভলির ১.১.১ এর সাথে মাল্টিপার্ট অনুরোধের অনুমতি দেয় এমন একটি শ্রেণীর কোটলিন সংস্করণ এখানে।

এটি বেশিরভাগ @ বিএনকে এর সমাধানের উপর ভিত্তি করে তবে খুব সহজ সরল। আমি কোনও বিশেষ পারফরম্যান্স ইস্যু লক্ষ্য করি নি। আমি প্রায় 3 সেকেন্ডের মধ্যে একটি 5 এমবি পিক আপলোড করেছি।

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)
        }
    }
}

ওহে. আমরা উপরের শ্রেণিটি কীভাবে ব্যবহার করতে পারি সে সম্পর্কে আরও কোনও তথ্য? উদাহরণস্বরূপ আমরা কীভাবে এটির সাথে একটি .jpg চিত্র আপলোড করতে পারি?
থানাসিস

0

আমি মূল ভলির লাইব্রেরির একটি মোড়ক পেয়েছি যা মাল্টি-পার্ট অনুরোধের জন্য সংহত করা সহজ। এটি অন্যান্য অনুরোধ পরামিতিগুলির সাথে মাল্টি-পার্ট ডেটা আপলোড করাও সমর্থন করে। অতএব আমি আমার কোড ভবিষ্যতের বিকাশকারীদের জন্য ভাগ করছি যাঁরা আমার যে সমস্যার মুখোমুখি হতে পারেন (যেমন কিছু অন্যান্য পরামিতি সহ ভলির সাহায্যে মাল্টি পার্ট ডেটা আপলোড করা)।

build.gradleফাইলটিতে নিম্নলিখিত লাইব্রেরি যুক্ত করুন।

dependencies {
    compile 'dev.dworks.libs:volleyplus:+'
}

দয়া করে মনে রাখবেন, আমি আমার থেকে মূল ভলির লাইব্রেরিটি সরিয়েছিbuild.gradle এবং এর পরিবর্তে উপরের লাইব্রেরিটি ব্যবহার করেছি যা একই অংশে একীকরণের কৌশলযুক্ত বহু-অংশ এবং সাধারণ অনুরোধ উভয়ই পরিচালনা করতে পারে।

তারপরে আমাকে কেবল নিম্নলিখিত ক্লাসটি লিখতে হয়েছিল যা পোষ্ট অনুরোধ অপারেশন পরিচালনা করে।

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);

আপনি এই লাইব্রেরিটি ব্যবহার করে একবারে একটি ফাইল আপলোড করতে পারেন। তবে আমি একাধিক ফাইল আপলোড করার ব্যবস্থা করতে পারি, কেবল একাধিক কাজ শুরু করে tasks

আশা করি এইটি কাজ করবে!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.