当前位置:首页 > 行业动态 > 正文

安卓上传图片文件到服务器

安卓上传图片至服务器需先获取文件路径,通过MultipartEntityBuilder构造请求体,使用OkHttp/Retrofit发起POST请求,需申请存储权限并配置 服务器接收接口

权限处理

安卓系统对文件读写和网络操作需要动态申请权限,需在AndroidManifest.xml中声明权限并在运行时请求。

权限类型 用途 声明方式
READ_EXTERNAL_STORAGE 读取本地图片 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
WRITE_EXTERNAL_STORAGE 保存拍摄的图片(Android 10以下) <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
CAMERA 使用摄像头拍摄图片 <uses-permission android:name="android.permission.CAMERA"/>
INTERNET 网络请求 <uses-permission android:name="android.permission.INTERNET"/>

动态申请权限示例代码:

// 检查并请求权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
    ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
}

获取图片文件

通过两种方式获取图片:从相册选择或调用摄像头拍摄。

安卓上传图片文件到服务器  第1张

从相册选择图片

Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);

调用摄像头拍摄图片

// 创建保存图片的文件
File imageFile = new File(Environment.getExternalStorageDirectory(), "temp_image.jpg");
Uri imageUri = Uri.fromFile(imageFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE);

处理选中的图片

onActivityResult中获取图片路径或URI,并转换为File对象。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        Uri imageUri = data.getData(); // 相册选择
        // 或直接使用拍摄的URI
        File imageFile = new File(imageUri.getPath());
        // 后续上传逻辑
    }
}

图片压缩(可选)

上传前压缩图片可减少流量消耗,推荐使用第三方库(如Luban、TinyPNG)。

示例(Luban):

Luban.with(this)
    .load(imageFile)
    .setCompressListener(new OnCompressListener() {
        @Override
        public void onStart() {}
        @Override
        public void onSuccess(File compressedFile) {
            // 使用压缩后的文件上传
            uploadFile(compressedFile);
        }
        @Override
        public void onError(Throwable e) {}
    }).launch();

上传图片到服务器

使用RetrofitOkHttp上传文件,需将图片封装为MultipartBody

添加依赖

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

定义API接口

public interface ApiService {
    @Multipart
    @POST("upload/image") // 替换为实际接口地址
    Call<ResponseBody> uploadImage(@Part MultipartBody.Part image);
}

构建请求体并上传

// 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://yourserver.com/") // 替换为实际服务器地址
    .addConverterFactory(GsonConverterFactory.create())
    .build();
ApiService apiService = retrofit.create(ApiService.class);
// 构建文件请求体
RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpeg"), imageFile);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", imageFile.getName(), requestBody);
// 发送请求
Call<ResponseBody> call = apiService.uploadImage(part);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        if (response.isSuccessful()) {
            // 上传成功
        } else {
            // 处理错误(如服务器返回400/500)
        }
    }
    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        // 网络错误处理
    }
});

注意事项

问题 解决方案
安卓10以上存储权限限制 使用Scoped Storage或通过ContentResolver访问媒体文件(推荐使用MediaStore API)
大文件上传失败 压缩图片或分片上传(Chunked Upload)
服务器接口不兼容 确认服务器支持multipart/form-data,且字段名与客户端一致(如file

相关问题与解答

问题1:如何选择适合的图片压缩库?

解答:
根据项目需求选择:

  • Luban:平衡压缩质量和速度,支持自定义配置。
  • TinyPNG:压缩质量高,但可能损失部分透明度信息(仅支持PNG)。
  • 手动压缩:使用Bitmapcompress()方法,但需处理内存占用问题。
    推荐优先使用成熟库,减少开发成本。

问题2:如何实现上传进度显示?

解答:
通过OkHttpProgressRequestBodyRetrofitProgressListener实现。
示例(OkHttp):

// 自定义ProgressRequestBody
class ProgressRequestBody extends RequestBody {
    private final RequestBody delegate;
    private final ProgressListener listener;
    public ProgressRequestBody(RequestBody delegate, ProgressListener listener) {
        this.delegate = delegate;
        this.listener = listener;
    }
    @Override public MediaType contentType() { return delegate.contentType(); }
    @Override public long contentLength() throws IOException { return delegate.contentLength(); }
    @Override public void writeTo(BufferedSink sink) throws IOException {
        // 写入数据时回调进度
    }
}
0