You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用AsyncTask实现Android向图片情感识别API上传图片并获取响应

我来帮你搞定Android端上传图片到情绪识别API并获取结果的功能!结合你已有的AsyncTask基础,我们分步骤来实现:


1. 实现支持文件上传的AsyncTask

你的API需要接收multipart/form-data格式的POST请求(对应cURL里的-F参数),我们修改AsyncTask来构造这种请求体,同时添加回调方便UI层处理结果:

public class ImageUploadTask extends AsyncTask<String, Void, String> {

    private OnResultListener resultListener;

    // 定义回调接口,把结果传递到UI层
    public interface OnResultListener {
        void onSuccess(JSONObject emotionResult);
        void onFailure(String errorMsg);
    }

    public ImageUploadTask(OnResultListener listener) {
        this.resultListener = listener;
    }

    @Override
    protected String doInBackground(String... params) {
        String apiUrl = params[0];
        String imagePath = params[1];
        String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"; // 请求体分隔符

        try {
            URL url = new URL(apiUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

            // 构建请求体
            DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
            // 添加文件参数
            dos.writeBytes("--" + boundary + "\r\n");
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + new File(imagePath).getName() + "\"\r\n");
            dos.writeBytes("Content-Type: image/jpeg\r\n\r\n");

            // 写入图片文件内容
            FileInputStream fis = new FileInputStream(imagePath);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, bytesRead);
            }
            fis.close();

            dos.writeBytes("\r\n--" + boundary + "--\r\n");
            dos.flush();
            dos.close();

            // 获取响应
            int statusCode = conn.getResponseCode();
            if (statusCode == 200) {
                InputStream inputStream = new BufferedInputStream(conn.getInputStream());
                return convertInputStreamToString(inputStream);
            } else {
                return "请求失败:HTTP状态码 " + statusCode;
            }

        } catch (Exception e) {
            e.printStackTrace();
            return "请求异常:" + e.getLocalizedMessage();
        }
    }

    @Override
    protected void onPostExecute(String response) {
        super.onPostExecute(response);
        if (resultListener == null) return;

        try {
            // 解析返回的JSON结果
            JSONObject resultJson = new JSONObject(response);
            resultListener.onSuccess(resultJson);
        } catch (JSONException e) {
            resultListener.onFailure("解析响应失败:" + e.getMessage());
        }
    }

    // 复用你原来的流转字符串方法
    private String convertInputStreamToString(InputStream inputStream) {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}

关键说明:

  • multipart/form-data格式构造请求,完全匹配你的cURL调用逻辑
  • 新增回调接口OnResultListener,解决AsyncTask无法直接更新UI的问题
  • 复用了你原有的convertInputStreamToString方法,减少重复代码

2. 实现图片选择功能(相机+本地图库)

接下来要让用户能选择图片,我们调用系统Intent并处理返回结果:

2.1 配置必要权限

AndroidManifest.xml中添加权限:

<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 读取本地图片权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明相机功能(可选) -->
<uses-feature android:name="android.hardware.camera" android:required="false" />

注意:Android 6.0+需要动态申请READ_EXTERNAL_STORAGECAMERA权限,你需要自行添加权限检查逻辑。

2.2 调用相机/图库的方法

在你的Activity中添加以下代码:

private static final int REQUEST_GALLERY = 100;
private static final int REQUEST_CAMERA = 101;
private String cameraImagePath; // 保存相机拍照后的图片路径

// 打开本地图库
private void openGallery() {
    Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, REQUEST_GALLERY);
}

// 打开相机拍照
private void openCamera() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // 创建临时文件保存拍照结果
    File tempImage = createTempImageFile();
    if (tempImage != null) {
        cameraImagePath = tempImage.getAbsolutePath();
        // Android7.0+需要用FileProvider获取Uri
        Uri imageUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", tempImage);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, REQUEST_CAMERA);
    }
}

// 创建临时图片文件
private File createTempImageFile() {
    try {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
        String fileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        return File.createTempFile(fileName, ".jpg", storageDir);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

配置FileProvider(Android7.0+必填)

AndroidManifest.xmlapplication标签内添加:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

然后在res/xml目录下创建file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_images" path="Pictures" />
</paths>

2.3 处理图片选择结果

重写Activity的onActivityResult方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        String imagePath = null;
        if (requestCode == REQUEST_GALLERY) {
            // 处理图库选择的图片
            Uri uri = data.getData();
            imagePath = getRealPathFromUri(uri);
        } else if (requestCode == REQUEST_CAMERA) {
            // 处理相机拍照的图片
            imagePath = cameraImagePath;
        }

        if (imagePath != null) {
            // 启动上传任务
            uploadImageToApi(imagePath);
        }
    }
}

// 将Uri转换为真实文件路径
private String getRealPathFromUri(Uri uri) {
    String[] projection = {MediaStore.Images.Media.DATA};
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
    if (cursor == null) return null;
    int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    String path = cursor.getString(columnIndex);
    cursor.close();
    return path;
}

3. 启动上传任务并处理结果

最后实现uploadImageToApi方法,启动AsyncTask并处理返回的情绪数据:

private void uploadImageToApi(String imagePath) {
    String apiUrl = "http://192.168.0.101:5000/Img_Emotion/";
    new ImageUploadTask(new ImageUploadTask.OnResultListener() {
        @Override
        public void onSuccess(JSONObject result) {
            // 在UI层处理成功结果
            try {
                double happy = result.getDouble("happy");
                double neutral = result.getDouble("neutral");
                double angry = result.getDouble("angry");
                // 其他情绪字段同理
                Log.d("情绪识别结果", "开心值:" + happy + ",中性值:" + neutral);
                
                // 更新UI(需要在主线程执行)
                runOnUiThread(() -> {
                    // 比如用TextView显示结果
                    // tvResult.setText("开心:" + happy + "%");
                });
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(String errorMsg) {
            // 处理失败情况
            Log.e("上传错误", errorMsg);
            runOnUiThread(() -> {
                // Toast.makeText(MainActivity.this, "上传失败:" + errorMsg, Toast.LENGTH_SHORT).show();
            });
        }
    }).execute(apiUrl, imagePath);
}

注意事项
  • 确保Android设备和你的Web服务在同一个局域网内(因为用的是本地IP192.168.0.101
  • Android9.0+默认禁止明文HTTP请求,需要在AndroidManifest.xmlapplication标签中添加android:usesCleartextTraffic="true"
  • 记得处理权限申请,避免因权限不足导致图片无法读取或相机无法打开

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

火山引擎 最新活动