上一篇
安卓上传file图片到服务器
- 行业动态
- 2025-04-24
- 2939
使用MultipartEntityBuilder构建请求,添加FileBody文件参数,设置Content-Type为image/jpeg,通过HttpClient执行POST请求,处理服务器响应获取上传结果,需配置网络权限及文件读取权限
权限处理
安卓系统需要动态申请存储权限(读取/写入外部存储)
权限类型 | 申请方式 | 适用场景 |
---|---|---|
READ_EXTERNAL_STORAGE | Manifest.permission.READ_EXTERNAL_STORAGE | 读取本地图片 |
WRITE_EXTERNAL_STORAGE | Manifest.permission.WRITE_EXTERNAL_STORAGE | 保存缓存图片(可选) |
READ_MEDIA_IMAGES | Manifest.permission.READ_MEDIA_IMAGES | Android 13+ 替代方案 |
// 检查并请求权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE); }
图片选择方式
通过系统相册选择
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
通过文件管理器选择
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/"); startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
处理选中的图片
数据类型 | 处理方式 | 注意事项 |
---|---|---|
Uri(Android 11+) | 直接使用URI上传 | 不需要转换路径 |
Uri(Android 10-) | 通过ContentResolver获取真实路径 | 需要处理不同厂商的URI格式 |
Bitmap | 压缩后转为File | 注意内存占用 |
// 获取文件路径(Android 10以下) public String getRealPathFromURI(Uri uri) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(uri, projection, null, null, null); if (cursor != null) { cursor.moveToFirst(); String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); cursor.close(); return path; } return null; }
图片上传实现
使用OkHttp上传
OkHttpClient client = new OkHttpClient(); // 创建请求体 MultipartBody body = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", fileName, RequestBody.create(mediaType, file)) .build(); // 创建请求 Request request = new Request.Builder() .url("https://yourserver.com/upload") .post(body) .build(); // 异步执行 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 处理失败 } @Override public void onResponse(Call call, Response response) throws IOException { // 处理成功 } });
服务器端接收示例
技术栈 | 关键代码 | 文件保存位置 |
---|---|---|
Spring Boot | @RequestParam MultipartFile file | /uploads/ 目录 |
Flask | request.files['file'] | upload/ 目录 |
Express | multer 中间件 | public/uploads/ 目录 |
// Spring Boot示例 @PostMapping("/upload") public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) { try { String path = "/uploads/" + file.getOriginalFilename(); file.transferTo(new File(path)); return ResponseEntity.ok("上传成功"); } catch (IOException e) { return ResponseEntity.status(500).body("上传失败"); } }
常见问题解决方案
问题现象 | 解决方案 | 代码示例 |
---|---|---|
权限被拒绝 | 添加权限声明和动态申请 | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> |
文件路径为空 | 使用ContentResolver获取InputStream | InputStream is = getContentResolver().openInputStream(uri); |
上传失败404 | 检查服务器接口地址 | 确保URL包含协议(http/https) |
大文件上传慢 | 显示进度对话框 | 使用ProgressBar 配合onResponse 回调 |
相关问题与解答
Q1:如何压缩图片以减少上传时间?
A1:使用BitmapFactory.Options进行采样压缩:
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, new BitmapFactory.Options()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); // 质量压缩到80% byte[] bytes = baos.toByteArray();
Q2:如何处理Android 11+的分区存储限制?
A2:使用Scoped Storage适配方案:
- 使用
MediaStore.createAtomicFile()
保存临时文件 - 通过
ACTION_OPEN_DOCUMENT
获取持久化访问权限 - 使用
ContentResolver
直接操作URI而非文件