Android 13 Как сохранить изображение в галерее .Net MAUI

Я создаю проект в .Net MAUI, который делает фотографии, и я потрачу деньги на его сохранение в галерее, создав папку.

Я видел, что Google изменил свои разрешения.

<uses-permission android:name = "android.permission.CAMERA" />
<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion = "32" />
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion = "32" />
<uses-permission android:name = "android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- Required only if your app needs to access images or photos that other apps created -->
<uses-permission android:name = "android.permission.READ_MEDIA_IMAGES" />
<!-- Required only if your app needs to access videos that other apps created -->
<uses-permission android:name = "android.permission.READ_MEDIA_VIDEO" />
<queries>
    <intent>
        <action android:name = "android.media.action.IMAGE_CAPTURE" />
    </intent>
</queries>

Это код:

string sGpath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

// Crear la ruta completa del archivo
var folderPath = Path.Combine(sGpath, "GPV2");

photo.FileName = $ "{vf.IdVisita}_{DateTime.Now:yy_MM_dd_HH_mm_ss}.jpg";
string uniqueFileName = $ "{vf.IdVisita}_{DateTime.Now:yy_MM_dd_HH_mm_ss}.jpg";

string filePath = Path.Combine(folderPath, photo.FileName);

if (!Directory.Exists(folderPath)) {
  Directory.CreateDirectory(folderPath);
}

using Stream sourceStream = await photo.OpenReadAsync();

Console.WriteLine($ "Archivo guardado en {filePath}");

vf.Titulo = uniqueFileName;
using(FileStream fileStream = File.OpenRead(filePath)) {
  vf.Foto = ReadFully(fileStream);
}

Путь, по которому он сохраняется для меня,

/data/user/0/com.companyname.photov2/files/Pictures/GPV2/1_24_05_30_10_53_48.jpg

Даже несмотря на это, я не удерживаюсь в галерее, как мне это сделать?

Вы не можете просто записать в системную папку. Android предоставляет API-интерфейсы Media для добавления изображений в системную галерею.

Jason 30.05.2024 11:50

@Jason Какой API может быть мне полезен? например Медиа-плагин?

Giorgio 30.05.2024 11:53
stackoverflow.com/questions/74073402/…
Jason 30.05.2024 12:07

Вы можете сделать снимок с помощью Android и сохранить его в галерее. Итак, в Android 11 и более поздних версиях вы будете запрашивать доступ к хранилищу из своего приложения, использовать пользовательскую папку и с помощью MediaPicker.CapturePhotoAsync сохранять файлы в этой папке. Таким образом, путь будет примерно таким: /storage/emulated/0/Pictures/GPV2/<unique_file_name>.jpg

Bob 30.05.2024 12:21

@BobSmith Как я могу запросить доступ к хранилищу? с Permissions.CheckStatusAsync<Permissions.Media>()? Это не работает

Giorgio 30.05.2024 12:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
410
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Используйте класс MediaPicker, чтобы запустить камеру и сделать снимок, не нужно усложнять процесс. Кроме того, вам не нужно было думать о том, как обращаться с устаревшими разрешениями. Если вы создали собственную папку, вам придется сохранить поток захваченных фотографий в пути к файлу, поместив его с помощью функции await photoStream.CopyToAsync(newFileStream).

using System.IO;
using System.Threading.Tasks;
using Microsoft.Maui.Media;
using Microsoft.Maui.Storage;

public async Task TakeAndSavePhoto()
{
    // Request storage access (Android 11+)
    if (Runtime.OperatingSystem == OperatingSystem.Android && Build.VERSION.SdkInt >= BuildVersionCodes.R)
    {
        var permissionStatus = await MediaLibrary.RequestPermissionsAsync();
        if (permissionStatus != MediaLibraryPermissionStatus.Granted)
        {
            // Handle permission denied scenario 
            return;
        }
    }

    // Create custom folder 
    string sGpath = Environment.GetFolderPath(Environment.SpecialFolder.Pictures);
    var folderPath = Path.Combine(sGpath, "GPV2");
    if (!Directory.Exists(folderPath))
    {
        Directory.CreateDirectory(folderPath);
    }

    // Capture photo
    FilePickerFileType captureFileType = new FilePickerFileType(PickFileType.Image);
    var photo = await MediaPicker.CapturePhotoAsync(captureFileType);

    if (photo == null)
    {
        // Handle user cancellation or error 
        return;
    }

    // Save photo to custom folder 
    string uniqueFileName = $"{DateTime.Now:yy_MM_dd_HH_mm_ss}.jpg";
    string filePath = Path.Combine(folderPath, uniqueFileName);
    if (!string.IsNullOrEmpty(folderPath))
    {
        using (var newFileStream = File.OpenWrite(filePath))
        {
            await photo.OpenReadAsync().CopyToAsync(newFileStream);
        }
    }

    // Save photo to public gallery
    using (var photoStream = await photo.OpenReadAsync())
    {
        await MediaLibrary.SaveToAlbumAsync(photoStream, "GPV2 Photos"); // Replace with a descriptive album name
    }

    Console.WriteLine($"Photo saved to gallery: {filePath}"); // Optional, for debugging
}

Не забудьте обновить AndroidManifest.xml для Android 11 или более поздней версии.

<uses-permission android:name = "android.permission.MANAGE_EXTERNAL_STORAGE" />

Таким образом, путь будет примерно таким: /storage/emulated/0/Pictures/GPV2/<unique_file_name>.jpg


Обновление 1

Проверьте статус разрешения

var mediaPermissionStatus = await Permissions.CheckStatusAsync<Permissions.Media>();

Вот возможные результаты:

  • Permissions.PermissionStatus.Granted: Пользователь уже предоставил вашему приложению разрешение на хранение.
  • Permissions.PermissionStatus.Denied: Пользователь отклонил разрешение на хранение.
  • Permissions.PermissionStatus.Revoked: Пользователь предоставил разрешение в прошлом, но позже отозвал его.
  • Permissions.PermissionStatus.Unknown: Статус разрешения неизвестен.

Обновление 2

@Giorgio Чтобы это работало, вам нужно добавить READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE разрешение.

Также установите android:maxSdkVersion = "28" для WRITE_PERMISSION.

<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission
    android:name = "android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion = "28" />

Прочтите эту ссылку для получения дополнительной информации.

У меня есть эта ошибка: MediaLibrary, Runtime, BuildVersionCodes, PickFileType не существует.

Giorgio 30.05.2024 13:13

вам нужно импортировать эти пакеты

Bob 30.05.2024 13:20

например, для среды выполнения используется ObjCRuntime; и у тебя его нет

Giorgio 30.05.2024 13:26

@Giorgio Runtime — это встроенное пространство имен, поэтому импорт не требуется. MediaLibrary, PickFileType использует пакет Microsoft.Maui.Media и BuildVersionCodes использует пакет Microsoft.Maui.Platform соответственно.

Bob 30.05.2024 14:25
Learn.microsoft.com/en-us/dotnet/api/… нет MediaLibrary
Giorgio 13.06.2024 12:12

@Джорджио, вы можете задать его как другой вопрос (поскольку он введен в инфраструктуру .NET 7.0), поскольку добавление этой части в мой ответ превратит его в слишком широкую категорию и запутает будущих читателей.

Bob 13.06.2024 13:38

может кто-нибудь из модераторов поможет мне так выразиться?

Giorgio 13.06.2024 13:44
Ответ принят как подходящий

Вы можете использовать следующий код, чтобы сохранить поток изображений в галерее вашего устройства Android.

    public static class ImageSaveHelper
    {
        public static async Task SaveImage(Stream imageStream)
        {
            using var stream = imageStream;
            using var memoryStream = new MemoryStream();
            stream.CopyTo(memoryStream);

            stream.Position = 0;
            memoryStream.Position = 0;

#if ANDROID
var context = Platform.CurrentActivity;

    if (OperatingSystem.IsAndroidVersionAtLeast(29))
    {
        Android.Content.ContentResolver resolver = context.ContentResolver;
        Android.Content.ContentValues contentValues = new();
        contentValues.Put(Android.Provider.MediaStore.IMediaColumns.DisplayName, "image.png");
        contentValues.Put(Android.Provider.MediaStore.IMediaColumns.MimeType, "image/png");
        contentValues.Put(Android.Provider.MediaStore.IMediaColumns.RelativePath, "DCIM/" + "image");
        Android.Net.Uri imageUri = resolver.Insert(Android.Provider.MediaStore.Images.Media.ExternalContentUri, contentValues);
           
        var os = resolver.OpenOutputStream(imageUri);
        Android.Graphics.BitmapFactory.Options options = new();
        options.InJustDecodeBounds = true;
        var bitmap = Android.Graphics.BitmapFactory.DecodeStream(stream);
        bitmap.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 100, os);
        os.Flush();
        os.Close();
    }
    else
    {
        Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
        string path = System.IO.Path.Combine(storagePath.ToString(), "image.png");
        System.IO.File.WriteAllBytes(path, memoryStream.ToArray());
        var mediaScanIntent = new Android.Content.Intent(Android.Content.Intent.ActionMediaScannerScanFile);
        mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
        context.SendBroadcast(mediaScanIntent);
    }

#endif
        }

    }

Примечание:

Как я могу запросить доступ к хранилищу? с Permissions.CheckStatusAsync<Permissions.Media>()? Это не работает

Вам необходимо добавить следующие разрешения, если вы хотите сохранять и читать изображения в Галерее, а не Permissions.Media.

<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-sdk android:minSdkVersion = "21" android:targetSdkVersion = "31" />

Из документа Разрешения мы знаем, что разрешение Permissions.Media всегда будет возвращать значение «Предоставлено», если оно проверено или запрошено.

И еще вам понадобится следующее API на мауи:

PermissionStatus statusread = await Permissions.RequestAsync<Permissions.StorageRead>();
PermissionStatus statuswrite = await Permissions.RequestAsync<Permissions.StorageWrite>();

Есть идеи, как это сделать, но в iOS?

Giorgio 13.06.2024 13:12

Другие вопросы по теме