Android 保存位图到图像文件

4

如何将位图保存为图像文件?(png / jpg任意类型均可...)

我正在运行设备(海思芯片)和Android应用程序。
Android和设备通过套接字进行通信。
设备发送图像(h.264),并在TextureView上显示它在Android上。
我可以使用textureView.getBitmap()从TextureView获取位图。


我创建了一个按钮来保存图片,使用textureView.getBitmap()

<Button
  android:id="@+id/btnSavePng"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:onClick="onBtnSavePng"
  android:text="savePicture" />

而且,onclick函数就像下面这样。


public String getCurrentTimeString() {
  int yyyy = Calendar.getInstance().get(Calendar.YEAR);
  int MM = Calendar.getInstance().get(Calendar.MONTH) + 1;
  int dd = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
  int hh = Calendar.getInstance().get(Calendar.HOUR);
  int mm = Calendar.getInstance().get(Calendar.MINUTE);
  int ss = Calendar.getInstance().get(Calendar.SECOND);
  
  String result = yyyy+"-"+MM+"-"+dd+" "+hh+":"+mm+":"+ss;
  return result;
}

public void onBtnSavePng(View view) {
  try {
    File storage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
    String fname = getCurrentTimeString() + ".jpg";
    File tp = new File(storage, fname);
    Bitmap bm = textureView.getBitmap();
    tp.createNewFile(); // Result of File.createNewFile() ignored

    FileOutputStream ot = new FileOutputStream(tp);
    bm.compress(Bitmap.CompressFormat.JPEG, 100, ot);
    ot.close();
  } catch(Exception e) {
    Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
  }
}

我允许使用像下面这样的AndroidManifest.xml中的权限,而android:requestLegacyExternalStorage="true"在应用程序上。

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

我的代码有问题吗?

如果有问题,如何将位图保存为PNG或JPG文件?

我猜我的应用程序无法访问目录。

谢谢阅读我的问题。


问题已自行解决。


public void onBtnSavePng(View view) {
  try {
    String root = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES).toString();
    File myDir = new File(root + "/saved_images");
    myDir.mkdirs();
    String fname = getCurrentTimeString() + ".jpg";
    File file = new File(myDir, fname);

    FileOutputStream out = new FileOutputStream(file);
    Bitmap bm = textureView.getBitmap();
    bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
    out.flush();
    out.close();
  } catch( Exception e) {
    Log.d("onBtnSavePng", e.toString());
  }

我不明白为什么我的代码还没有起作用。
无论如何,谢谢所有帮助我的人。
我会尝试所有的回复代码。

谢谢。


那么,有什么区别呢? - blackapps
4个回答

2

1. 如果您还没有请求运行时权限,请勾选它:https://developer.android.com/training/permissions/requesting

2. 或者如果您的安卓版本高于10: https://developer.android.com/about/versions/11/privacy/storage#scoped-storage

  • 在将应用更新为针对 Android 11 的目标版本后,系统会忽略 requestLegacyExternalStorage 标志。

然后您必须使用 SAF 或 MediaStore API 将位图存储在“公共目录”中。

SAF: https://developer.android.com/guide/topics/providers/document-provider

MediaStore API: https://developer.android.com/reference/android/provider/MediaStore(媒体存储API)

public void onBtnSavePng(View view) {
    try {
        String fileName = getCurrentTimeString() + ".jpg";

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/");
            values.put(MediaStore.MediaColumns.IS_PENDING, 1);
        } else {
            File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
            File file = new File(directory, fileName);
            values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
        }

        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

        try (OutputStream output = getContentResolver().openOutputStream(uri)) {
            Bitmap bm = textureView.getBitmap();
            bm.compress(Bitmap.CompressFormat.JPEG, 100, output);
        }
    } catch (Exception e) {
        Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
    }
}

谢谢。 1. 我请求并接受运行时权限。// 2. 我不确定,所以我会检查一下。// 3. 我从未使用过SAF、MediaStore API。我会尝试一下...在上传这个问题后,我用另一种方式解决了它(不同的代码),所以我会更改我的问题文本。谢谢。 - Zem

1

在Kotlin中 这对我有效 ->

fun saveMediaToStorage(bitmap: Bitmap, context: Context, onUriCreated: (Uri) -> Unit) {
var fos: OutputStream? = null

//Generating a file name
val filename = "${System.currentTimeMillis()}.jpg"

try {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
        val resolver = context.contentResolver
        val contentValues = ContentValues()
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$filename.jpg")

        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
        val imageUri =
            resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
        fos = Objects.requireNonNull(imageUri)?.let {
            resolver.openOutputStream(it)
        }

        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
        Objects.requireNonNull(fos)

        imageUri?.let { onUriCreated(it) }
    } else {
        //These for devices running on android < Q
        val imagesDir =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
        val image = File(imagesDir, filename)
        fos = FileOutputStream(image)

        fos.use {
            //Finally writing the bitmap to the output stream that we opened
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
        }

        onUriCreated(Uri.fromFile(image))
    }
} catch (e: Exception) {
    Log.d("error", e.toString())
}

}


你有一个代码示例可以说明如何使用这个函数吗? - anta40

0
 String myDir =new File (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)+pdfDirectoryImage);
            fileName = filename + ".png";
            androidElevenPath = saveImages(mContext,finalBitmap,filename);

 private static Uri saveImages(Context mContext, Bitmap bitmap, @NonNull String name) throws IOException {
        boolean saved;
        OutputStream fos;
        File image = null;
        Uri imageUri = null;
        String imagesDir = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ContentResolver resolver = mContext.getContentResolver();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
            contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM" + pdfDirectoryImage);
             imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            fos = resolver.openOutputStream(imageUri);
        } else {
             imagesDir = Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_DCIM).toString() + File.separator + pdfDirectoryImage;

            File file = new File(imagesDir);

            if (!file.exists()) {
                file.mkdir();
            }

             image = new File(imagesDir, name + ".png");
            fos = new FileOutputStream(image);

        }

        saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
        return imageUri;
    }

1
虽然这段代码可能解决了问题,但是在您的帖子中包含一个解释(//meta.stackexchange.com/q/114762)来阐明其解决问题的方式和原理将有助于提高您帖子的质量,并可能导致更多的赞同。请记住,您的回答是为将来的读者而提供,而不仅仅是为了现在提问的人。请[编辑]您的答案以添加解释并说明适用的限制和假设。 - Adrian Mole

0

如果您正在使用Android11+,并且已经授予了存储管理权限,并且直接处理文件(而不是使用MediaStore API),那么问题可能是在创建的文件名中包含了“:”字符(通常是时间戳)。与旧版Android不同,Android 11不允许这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接