Android开发:选图/拍照后如何将图片缩放到1000×600再上传?
嘿,我帮你梳理并完善了这个图片选择、拍照、缩放上传的功能实现,补上了原代码缺失的部分,特别是上传前将图片缩放到1000×600的核心逻辑。下面是完整的方案:
功能需求
- 支持从系统相册选择图片
- 调用设备相机拍摄图片
- 预览选中/拍摄的图片
- 上传前将图片统一缩放为1000×600尺寸
- 将处理后的图片上传至服务器
完整实现代码
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.FileProvider; import com.afollestad.materialdialogs.MaterialDialog; import com.bumptech.glide.Glide; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class ImageActivity extends AppCompatActivity implements View.OnClickListener { ImageView imageView; Button pickImage, upload; private static final int REQUEST_PICK_PHOTO = 2; private static final String TAG = ImageActivity.class.getSimpleName(); private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100; private Uri fileUri; private String mImageFileLocation = ""; private String postPath; // 缩放目标尺寸 private static final int TARGET_WIDTH = 1000; private static final int TARGET_HEIGHT = 600; // 权限请求码 private static final int REQUEST_PERMISSIONS = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.image_layout); imageView = findViewById(R.id.preview); pickImage = findViewById(R.id.pickImage); upload = findViewById(R.id.upload); pickImage.setOnClickListener(this); upload.setOnClickListener(this); // 检查权限 checkPermissions(); } @Override public void onClick(final View v) { switch (v.getId()) { case R.id.pickImage: new MaterialDialog.Builder(this) .title(R.string.uploadImages) .items(R.array.uploadImages) .itemsIds(R.array.itemIds) .itemsCallback((dialog, view, which, text) -> { switch (which) { case 0: // 选择相册图片 Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(galleryIntent, REQUEST_PICK_PHOTO); break; case 1: // 调用相机 if (isDeviceSupportCamera()) { captureImage(); } else { Toast.makeText(this, "设备不支持相机", Toast.LENGTH_SHORT).show(); } break; case 2: // 清空预览 imageView.setImageResource(R.drawable.ic_launcher_background); postPath = null; break; } }) .show(); break; case R.id.upload: if (postPath != null) { // 先缩放图片再上传 String scaledImagePath = scaleImage(postPath); if (scaledImagePath != null) { uploadFile(scaledImagePath); } else { Toast.makeText(this, "图片缩放失败", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "请先选择或拍摄图片", Toast.LENGTH_SHORT).show(); } break; } } /** * 检查并申请必要权限 */ private void checkPermissions() { String[] permissions = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA }; boolean needRequest = false; for (String perm : permissions) { if (ActivityCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) { needRequest = true; break; } } if (needRequest) { ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSIONS); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (!allGranted) { Toast.makeText(this, "需要授予权限才能使用功能", Toast.LENGTH_SHORT).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == REQUEST_PICK_PHOTO) { if (data != null) { // 获取相册图片路径 Uri selectedImage = data.getData(); String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null); if (cursor != null) { cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String mediaPath = cursor.getString(columnIndex); // 预览图片 Glide.with(this).load(mediaPath).into(imageView); cursor.close(); postPath = mediaPath; } } } else if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) { // 预览拍摄的图片 if (Build.VERSION.SDK_INT > 21) { Glide.with(this).load(mImageFileLocation).into(imageView); postPath = mImageFileLocation; } else { Glide.with(this).load(fileUri).into(imageView); postPath = fileUri.getPath(); } } } else if (resultCode != RESULT_CANCELED) { Toast.makeText(this, "抱歉,操作出现错误!", Toast.LENGTH_LONG).show(); } } /** * 检查设备是否支持相机 */ private boolean isDeviceSupportCamera() { return getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); } /** * 调用相机拍摄图片 */ private void captureImage() { Intent callCameraApplicationIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File photoFile = null; try { photoFile = createImageFile(); } catch (IOException e) { Log.e(TAG, "生成图片文件失败: " + e.getMessage()); e.printStackTrace(); return; } // 适配Android 7.0+的FileProvider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { fileUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile); } else { fileUri = Uri.fromFile(photoFile); } callCameraApplicationIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); startActivityForResult(callCameraApplicationIntent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE); } /** * 创建图片文件 */ private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( imageFileName, ".jpg", storageDir ); mImageFileLocation = image.getAbsolutePath(); return image; } /** * 将图片缩放至目标尺寸1000×600 */ private String scaleImage(String originalPath) { try { // 先获取原图的宽高,避免加载大图内存溢出 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(originalPath, options); int originalWidth = options.outWidth; int originalHeight = options.outHeight; // 计算缩放比例,保持宽高比,确保不超过目标尺寸 float scaleWidth = (float) TARGET_WIDTH / originalWidth; float scaleHeight = (float) TARGET_HEIGHT / originalHeight; float scale = Math.min(scaleWidth, scaleHeight); // 加载原图并缩放 options.inJustDecodeBounds = false; options.inSampleSize = calculateInSampleSize(options, TARGET_WIDTH, TARGET_HEIGHT); Bitmap originalBitmap = BitmapFactory.decodeFile(originalPath, options); Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, (int) (originalWidth * scale), (int) (originalHeight * scale), true); // 保存缩放后的图片到本地 String scaledFileName = "scaled_" + System.currentTimeMillis() + ".jpg"; File scaledFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), scaledFileName); FileOutputStream out = new FileOutputStream(scaledFile); scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out); out.flush(); out.close(); // 回收Bitmap内存 if (originalBitmap != scaledBitmap) { originalBitmap.recycle(); } scaledBitmap.recycle(); return scaledFile.getAbsolutePath(); } catch (Exception e) { Log.e(TAG, "图片缩放失败: " + e.getMessage()); e.printStackTrace(); return null; } } /** * 计算采样率,避免加载大图内存溢出 */ private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } /** * 上传图片到服务器 */ private void uploadFile(String filePath) { new Thread(() -> { try { File file = new File(filePath); OkHttpClient client = new OkHttpClient(); // 构建Multipart请求 RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("image", file.getName(), RequestBody.create(MediaType.parse("image/jpeg"), file)) .build(); Request request = new Request.Builder() .url("你的服务器上传接口地址") // 替换为实际的服务器接口 .post(requestBody) .build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { runOnUiThread(() -> { Toast.makeText(ImageActivity.this, "上传成功", Toast.LENGTH_SHORT).show(); }); } else { runOnUiThread(() -> { Toast.makeText(ImageActivity.this, "上传失败", Toast.LENGTH_SHORT).show(); }); } } catch (Exception e) { Log.e(TAG, "上传失败: " + e.getMessage()); e.printStackTrace(); runOnUiThread(() -> { Toast.makeText(ImageActivity.this, "上传出错", Toast.LENGTH_SHORT).show(); }); } }).start(); } }
关键注意事项
- 权限配置:在
AndroidManifest.xml中添加必要权限,并且配置FileProvider(适配Android 7.0+):
然后在<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" /> <application> <!-- FileProvider配置 --> <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> </application>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> - 服务器接口:替换
uploadFile()方法中的服务器地址为实际的上传接口 - 内存优化:缩放图片时使用
inSampleSize采样率,避免加载大图导致内存溢出 - 图片质量:压缩图片时可以调整
compress()方法的质量参数(0-100),平衡文件大小和画质
内容的提问来源于stack exchange,提问作者Dr.Kong




