上一篇
安卓上传图片文件到服务器
- 行业动态
- 2025-04-24
- 2616
安卓上传图片至服务器需先获取文件路径,通过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); }
获取图片文件
通过两种方式获取图片:从相册选择或调用摄像头拍摄。
从相册选择图片
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();
上传图片到服务器
使用Retrofit
或OkHttp
上传文件,需将图片封装为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)。
- 手动压缩:使用
Bitmap
的compress()
方法,但需处理内存占用问题。
推荐优先使用成熟库,减少开发成本。
问题2:如何实现上传进度显示?
解答:
通过OkHttp
的ProgressRequestBody
或Retrofit
的ProgressListener
实现。
示例(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 { // 写入数据时回调进度 } }