Ask me anything
Ithy Logo
Sign in to save

完整的Android文件选择与处理示例

跨设备与系统兼容的图片与PDF文件选择、压缩和Base64转换

android phone file selection and compression

关键亮点

  • 文件类型判断:准确识别并处理图片与PDF文件,确保文件的正确处理
  • 动态文件处理:对图片超出1MB的情况采用压缩策略,PDF超过4MB时提醒用户
  • Base64转换:无论文件类型,都可以安全转换为Base64格式,便于后续上传、存储或传输

概述

在本示例中,我们实现了一个Android应用程序,允许用户从手机文件夹选择图片或PDF文件。针对图片文件,我们判断其大小并在超过1MB时进行压缩处理;对于PDF文件,则检查文件大小是否超过4MB,若未超过则直接转换为Base64编码,否则以Toast消息告知用户。

此示例采用了现代Android中推荐的ActivityResultLauncher接口来避免使用过时的startActivityForResult方法,并且代码覆盖了多种兼容性要点,以适应各个型号设备和不同Android系统版本。


代码实现

Java代码示例

以下代码展示了如何在一个Activity中实现文件选择、图片压缩(当图片大小超过1MB时)、PDF大小判断和Base64转换。代码中包含了对于图片文件的压缩算法和PDF文件大小检测的逻辑,确保合理兼容各种Android系统的操作环境。

MainActivity.java


// 导入必要的包和类
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.DocumentsContract;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    private static final int MAX_IMAGE_SIZE = 1 * 1024 * 1024; // 1MB
    private static final int MAX_PDF_SIZE = 4 * 1024 * 1024; // 4MB
    private Button selectFileButton;
    private ImageView imageView;
    private ActivityResultLauncher<Intent> filePickerLauncher;
    private String base64Result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 使用activity_main.xml作为布局文件
        setContentView(R.layout.activity_main);

        // 初始化UI组件
        selectFileButton = findViewById(R.id.selectFileButton);
        imageView = findViewById(R.id.imageView);

        // 初始化ActivityResultLauncher来获取文件选择结果
        filePickerLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                    Uri fileUri = result.getData().getData();
                    if (fileUri != null) {
                        handleFile(fileUri);
                    }
                }
            }
        );

        // 设置按钮点击事件
        selectFileButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                openFilePicker();
            }
        });
    }

    // 打开文件选择器,设置支持的MIME类型
    private void openFilePicker() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        // 指定允许选择图片或者PDF文件
        String[] mimeTypes = {"image/*", "application/pdf"};
        intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        filePickerLauncher.launch(intent);
    }

    // 根据文件类型进行相应的处理
    private void handleFile(Uri fileUri) {
        String mimeType = getContentResolver().getType(fileUri);
        if(mimeType != null) {
            if (mimeType.startsWith("image/")) {
                processImage(fileUri);
            } else if (mimeType.equals("application/pdf")) {
                processPdf(fileUri);
            } else {
                Toast.makeText(this, "不支持的文件类型", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 处理图片文件:压缩并转换为Base64
    private void processImage(Uri imageUri) {
        try {
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
            // 判断并压缩图片,如果图片大于1MB则进行压缩处理
            Bitmap processedBitmap = compressImage(bitmap);
            // 显示压缩后的图片
            imageView.setImageBitmap(processedBitmap);

            // 将图片转换为Base64字符串
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            processedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteStream);
            byte[] imageBytes = byteStream.toByteArray();
            base64Result = Base64.encodeToString(imageBytes, Base64.DEFAULT);
            Toast.makeText(this, "图片已转换为Base64", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(this, "图片处理出现错误", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }

    // 图片压缩逻辑:如果图片大小超过设定的阈值,逐步降低图片质量至满足要求
    private Bitmap compressImage(Bitmap bitmap) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int quality = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        while (outputStream.toByteArray().length > MAX_IMAGE_SIZE && quality > 0) {
            outputStream.reset();
            quality -= 10;
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        }
        byte[] imageBytes = outputStream.toByteArray();
        return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
    }

    // 处理PDF文件:检查大小并转换为Base64
    private void processPdf(Uri pdfUri) {
        InputStream inputStream = null;
        try {
            inputStream = getContentResolver().openInputStream(pdfUri);
            if (inputStream == null) {
                Toast.makeText(this, "读取PDF失败", Toast.LENGTH_SHORT).show();
                return;
            }
            int fileSize = inputStream.available();
            if (fileSize > MAX_PDF_SIZE) {
                Toast.makeText(this, "PDF文件超过4MB", Toast.LENGTH_SHORT).show();
                return;
            }
            byte[] pdfBytes = new byte[fileSize];
            inputStream.read(pdfBytes);
            base64Result = Base64.encodeToString(pdfBytes, Base64.DEFAULT);
            Toast.makeText(this, "PDF已转换为Base64", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(this, "PDF处理出现错误", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        } finally {
            if(inputStream != null) {
                try { inputStream.close(); } catch(IOException e) { e.printStackTrace(); }
            }
        }
    }

    // 如果需要获取所选文件的实际路径,可以根据不同的Uri类型处理
    private String getRealPathFromURI(Uri uri) {
        String filePath = "";
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
        if(cursor != null) {
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            if(cursor.moveToFirst()) {
                filePath = cursor.getString(column_index);
            }
            cursor.close();
        }
        return filePath;
    }
}
  

布局文件

在布局XML文件中,我们至少需要一个按钮来启动文件选择器和一个ImageView来显示选中的图片,如下所示:

activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center">
    
    <Button
        android:id="@+id/selectFileButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="选择图片或PDF" />
    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="16dp"
        android:scaleType="centerCrop" />
</LinearLayout>
  

详细说明与注意事项

该示例代码涵盖了以下几个关键点:

1. 文件类型判断

利用内容解析器 (getContentResolver().getType()) 获取文件的MIME类型,根据MIME类型判断是图片还是PDF:

  • 图片文件:MIME类型以image/开头,对其进行压缩处理及Base64转换。
  • PDF文件:若MIME类型为application/pdf,则先判断文件大小再转换为Base64。

2. 图片压缩策略

为确保图片不超过1MB,我们采用了逐步降低压缩质量的方式。初始质量设为100,每次降低10,直到图片压缩后的字节数小于1MB。此策略既保证图片质量,又避免因过度压缩引起的失真。

3. PDF文件大小判断

使用InputStream.available()方法大致获取PDF文件的大小。当PDF文件超过4MB时,通过Toast提示用户文件过大,从而避免系统内存压力和编码失败问题。

4. Base64转换

将最终处理后的文件(图片或PDF)的字节数组传入Base64.encodeToString(),得到Base64编码的字符串,以便于网络上传、数据存储或者其他处理。这种方法为数据传输提供了安全、便捷的手段。

5. ActivityResultLauncher优势

使用ActivityResultLauncher替代过时的startActivityForResult,以保证在最新的AndroidX框架下工作更加稳定且具备更高的兼容性。这是一种现代化的处理方式,未来也更易于维护和升级。


兼容性与优化措施

代码中已考虑以下兼容性细节:

  • 通过明确指定MIME类型和使用Intent.EXTRA_MIME_TYPES满足不同Android版本文件选择器的要求。
  • 采用ActivityResultLauncher确保在最新和旧版Android系统中均能正确响应文件选取结果。
  • 借助动态压缩策略,对高分辨率图片进行处理,确保不因不同设备性能问题导致卡顿或内存溢出。
  • 针对PDF文件,实时判断文件大小,防止内存消耗过大,同时兼顾用户体验,及时反馈操作结果。

额外优化建议

为了进一步提高应用的鲁棒性和用户体验,您还可以考虑以下优化:

优化点 说明
错误处理 针对IO异常、文件为空等情况增加详细日志记录和用户反馈
多文件支持 支持一次选择多个文件并进行批量处理
UI提示 在图片压缩和转换过程中增加进度条或加载动画
第三方库 考虑集成更高效的图片压缩库如Luban或者Glide以提高压缩效率
文件路径获取 根据需要使用DocumentsContract和ContentResolver确保路径获取正确

资源与参考链接

以下链接提供了更多关于Android文件选择、图片处理及Base64转换的技术文档和示例,供进一步参考:


推荐的进一步查询

若您希望进一步提高应用相关知识,以下推荐的搜索查询将为您提供更多深入的技术文章与示例:


Last updated March 7, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI