You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何用Volley/AsyncTask的Multipart上传多张图片?

Hey Bharath! I see you're trying to adapt a single-image multipart upload to handle multiple images using Volley or AsyncTask, since Base64 wasn't working for you. Let's walk through working implementations for both approaches that should solve your problem.

Option 1: Using Volley with Custom Multipart Request

Volley is great for efficient network operations, but it doesn't natively support multipart requests—so we'll create a custom request class to handle multiple files.

Step 1: Add Volley Dependency

First, include Volley in your module-level build.gradle:

dependencies {
    implementation 'com.android.volley:volley:1.2.1'
}

Step 2: Create Custom MultipartRequest Class

This class extends Volley's Request to build multipart form data with text parameters and multiple files:

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MultipartRequest extends Request<String> {
    private final Response.Listener<String> mListener;
    private final Map<String, String> mParams;
    private final Map<String, File> mFileParams;
    private final String mBoundary;
    private static final String LINE_FEED = "\r\n";
    private static final String TWO_HYPHENS = "--";

    public MultipartRequest(int method, String url,
                            Response.Listener<String> listener,
                            Response.ErrorListener errorListener,
                            Map<String, String> params,
                            Map<String, File> fileParams) {
        super(method, url, errorListener);
        this.mListener = listener;
        this.mParams = params;
        this.mFileParams = fileParams;
        mBoundary = "apiclient-" + System.currentTimeMillis();
    }

    @Override
    public String getBodyContentType() {
        return "multipart/form-data; boundary=" + mBoundary;
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            // Add text parameters (if any)
            if (mParams != null && !mParams.isEmpty()) {
                for (Map.Entry<String, String> entry : mParams.entrySet()) {
                    bos.write((TWO_HYPHENS + mBoundary + LINE_FEED).getBytes());
                    bos.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINE_FEED).getBytes());
                    bos.write(LINE_FEED.getBytes());
                    bos.write(entry.getValue().getBytes());
                    bos.write(LINE_FEED.getBytes());
                }
            }

            // Add image files
            if (mFileParams != null && !mFileParams.isEmpty()) {
                for (Map.Entry<String, File> entry : mFileParams.entrySet()) {
                    String key = entry.getKey();
                    File file = entry.getValue();
                    String fileName = file.getName();

                    bos.write((TWO_HYPHENS + mBoundary + LINE_FEED).getBytes());
                    bos.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + fileName + "\"" + LINE_FEED).getBytes());
                    bos.write(("Content-Type: image/jpeg" + LINE_FEED).getBytes()); // Adjust for PNG if needed
                    bos.write(LINE_FEED.getBytes());

                    // Read and write file bytes
                    FileInputStream fis = new FileInputStream(file);
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        bos.write(buffer, 0, bytesRead);
                    }
                    fis.close();
                    bos.write(LINE_FEED.getBytes());
                }
            }

            // End multipart data
            bos.write((TWO_HYPHENS + mBoundary + TWO_HYPHENS + LINE_FEED).getBytes());
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw new AuthFailureError("Error building multipart request");
        }
    }

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

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }
}

Step 3: Use MultipartRequest in MainActivity

Here's how to integrate this into your MainActivity to upload multiple images:

package com.getspot.getspot.imagerestapi;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;

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

public class MainActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CODE = 100;
    private RequestQueue mRequestQueue;
    private final String UPLOAD_URL = "YOUR_SERVER_UPLOAD_ENDPOINT"; // Replace with your server URL

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

        mRequestQueue = Volley.newRequestQueue(this);

        Button uploadBtn = findViewById(R.id.upload_btn);
        uploadBtn.setOnClickListener(v -> {
            if (checkPermissions()) {
                uploadMultipleImages();
            } else {
                requestPermissions();
            }
        });
    }

    private boolean checkPermissions() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED;
    }

    private void requestPermissions() {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.INTERNET},
                PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                uploadMultipleImages();
            } else {
                Toast.makeText(this, "Permissions are required to upload images", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void uploadMultipleImages() {
        // Replace these with your actual image file paths
        File image1 = new File("/storage/emulated/0/Pictures/image1.jpg");
        File image2 = new File("/storage/emulated/0/Pictures/image2.jpg");
        File image3 = new File("/storage/emulated/0/Pictures/image3.jpg");

        // Optional text parameters
        Map<String, String> textParams = new HashMap<>();
        textParams.put("user_id", "123");
        textParams.put("description", "Multiple images upload via Volley");

        // File parameters (match key to your server's expected field name)
        Map<String, File> fileParams = new HashMap<>();
        fileParams.put("images[]", image1); // Use array key for multiple files
        fileParams.put("images[]", image2);
        fileParams.put("images[]", image3);

        MultipartRequest multipartRequest = new MultipartRequest(
                Request.Method.POST,
                UPLOAD_URL,
                response -> {
                    Log.d("UPLOAD_SUCCESS", response);
                    Toast.makeText(MainActivity.this, "Upload successful!", Toast.LENGTH_SHORT).show();
                },
                error -> {
                    Log.e("UPLOAD_ERROR", error.toString());
                    Toast.makeText(MainActivity.this, "Upload failed: " + error.getMessage(), Toast.LENGTH_SHORT).show();
                },
                textParams,
                fileParams
        );

        mRequestQueue.add(multipartRequest);
    }
}

Option 2: Using AsyncTask (Legacy but Reliable)

If you prefer AsyncTask over Volley, here's a working implementation using HttpURLConnection:

package com.getspot.getspot.imagerestapi;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_CODE = 100;
    private final String UPLOAD_URL = "YOUR_SERVER_UPLOAD_ENDPOINT"; // Replace with your server URL
    private final ArrayList<File> imageFiles = new ArrayList<>();

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

        // Add your image files to the list
        imageFiles.add(new File("/storage/emulated/0/Pictures/image1.jpg"));
        imageFiles.add(new File("/storage/emulated/0/Pictures/image2.jpg"));

        Button uploadBtn = findViewById(R.id.upload_btn);
        uploadBtn.setOnClickListener(v -> {
            if (checkPermissions()) {
                new UploadMultipleImagesTask().execute();
            } else {
                requestPermissions();
            }
        });
    }

    private boolean checkPermissions() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED;
    }

    private void requestPermissions() {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.INTERNET},
                PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                new UploadMultipleImagesTask().execute();
            } else {
                Toast.makeText(this, "Permissions required", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private class UploadMultipleImagesTask extends AsyncTask<Void, Void, String> {
        private static final String LINE_FEED = "\r\n";
        private static final String TWO_HYPHENS = "--";
        private final String boundary = "apiclient-" + System.currentTimeMillis();

        @Override
        protected String doInBackground(Void... voids) {
            HttpURLConnection conn = null;
            DataOutputStream dos = null;
            try {
                URL url = new URL(UPLOAD_URL);
                conn = (HttpURLConnection) url.openConnection();
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.setUseCaches(false);
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

                dos = new DataOutputStream(conn.getOutputStream());

                // Add text parameters
                addFormField(dos, "user_id", "123");
                addFormField(dos, "description", "Multiple images via AsyncTask");

                // Add images
                for (File file : imageFiles) {
                    addFilePart(dos, "images[]", file);
                }

                // End multipart data
                dos.writeBytes(TWO_HYPHENS + boundary + TWO_HYPHENS + LINE_FEED);
                dos.flush();
                dos.close();

                // Get server response
                int responseCode = conn.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) sb.append(line);
                    br.close();
                    return sb.toString();
                } else {
                    return "Server response code: " + responseCode;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return "Error: " + e.getMessage();
            } finally {
                if (conn != null) conn.disconnect();
            }
        }

        private void addFormField(DataOutputStream dos, String name, String value) throws Exception {
            dos.writeBytes(TWO_HYPHENS + boundary + LINE_FEED);
            dos.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"" + LINE_FEED);
            dos.writeBytes(LINE_FEED);
            dos.writeBytes(value + LINE_FEED);
        }

        private void addFilePart(DataOutputStream dos, String fieldName, File file) throws Exception {
            String fileName = file.getName();
            dos.writeBytes(TWO_HYPHENS + boundary + LINE_FEED);
            dos.writeBytes("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"" + LINE_FEED);
            dos.writeBytes("Content-Type: image/jpeg" + LINE_FEED);
            dos.writeBytes(LINE_FEED);

            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) dos.write(buffer, 0, bytesRead);
            fis.close();
            dos.writeBytes(LINE_FEED);
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            Log.d("UPLOAD_RESULT", result);
            if (result.startsWith("Error")) {
                Toast.makeText(MainActivity.this, "Upload failed: " + result, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "Upload successful!", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Key Notes:

  • Replace YOUR_SERVER_UPLOAD_ENDPOINT with your actual server URL that handles multipart form data.
  • Adjust the Content-Type (e.g., image/png) if uploading PNG images.
  • Ensure your server accepts multiple files using the key you specify (like images[] for array-based uploads).
  • Add these permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33"/>

内容的提问来源于stack exchange,提问作者Bharath S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:09:19