DevGang
Авторизоваться

Как добавить адаптер Backblaze к экземпляру Appwrite

Одной из наиболее важных особенностей Appwrite является его гибкость. Он предоставляет надежные инструменты, которые малые и крупные предприятия могут использовать для настройки серверной части с нуля или постепенного внедрения ее функций в существующую инфраструктуру.

В этом посте мы рассмотрим гибкость Appwrite за счет использования Appwrite Storage для управления файлами. Мы достигнем этого, создав приложение Flutter, которое использует адаптер Appwrite и Backblaze для управления изображениями. Репозиторий проекта можно найти здесь.

Обзор технологий

Backblaze — компания-разработчик программного обеспечения, которая обеспечивает онлайн-резервное копирование и управление хранением данных для изображений, видео, PDF-файлов и других типов файлов.

Помимо стандартной поддержки Appwrite для хранения файлов в локальном хранилище сервера, он также предоставляет адаптеры для плавной интеграции внешнего хранилища, такого как Backblaze.

Предварительные условия

Чтобы полностью усвоить концепции, представленные в этом руководстве, необходимо следующее:

  • Базовое понимание Dart и Flutter
  • Flutter SDK установлен
  • Docker установлен
  • Аккаунт Backblaze

Настройка сегмента хранилища на Backblaze

Чтобы начать, нам нужно войти в нашу учетную запись Backblaze, нажать кнопку Create a Bucket, ввести appwriteFlutter и создать.

Далее нам нужно создать Application Key. Application Key позволит нам безопасно подключить наш экземпляр Appwrite к Backblaze. Для этого перейдите на вкладку Application Key, нажмите кнопку Add a New Application Key, введите appwriteFlutter в качестве имени и нажмите Create.

После этого мы увидим экран, показывающий детали нашего приложения. Мы должны скопировать и сохранить keyID и applicationKey, поскольку они пригодятся при добавлении Backblaze в качестве адаптера.

Наконец, мы также должны скопировать и сохранить регион, в котором развернута наша корзина. Мы можем получить его, как показано ниже:

Настройте и создайте хранилище в Appwrite

Чтобы начать, нам нужно создать экземпляр Appwrite в предпочтительном каталоге, выполнив команду ниже:

docker run -it --rm \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
    --entrypoint="install" \
    appwrite/appwrite:1.3.8

При создании мы должны увидеть папку appwrite с двумя файлами.

Затем нам нужно добавить Backblaze в качестве адаптера, изменив переменные среды в файле .env, как показано ниже:

_APP_STORAGE_DEVICE=Backblaze
_APP_STORAGE_BACKBLAZE_ACCESS_KEY=<REPLACE WITH KEYID>
_APP_STORAGE_BACKBLAZE_SECRET=<REPLACE WITH APPLICATIONKEY>
_APP_STORAGE_BACKBLAZE_REGION=<REPLACE WITH REGION>
_APP_STORAGE_BACKBLAZE_BUCKET=appwriteFlutter

Наконец, нам нужно синхронизировать изменения, которые мы внесли в файл .env, с нашим сервером Appwrite. Для этого мы должны запустить команду ниже внутри каталога appwrite.

docker-compose up -d

Настройка проекта в Appwrite

Чтобы начать, нам нужно перейти к указанному имени хоста и порту http://localhost:80, войти в систему, нажать кнопку Create Project, ввести appwrite_backblaze в качестве имени, а затем Create.

Добавить поддержку платформы

Чтобы добавить поддержку нашего приложения Flutter, перейдите в меню Overview и нажмите кнопку Flutter App.

Далее мы должны изменить приложение Flutter, как описано ниже:

Чтобы получить наш идентификатор пакета, перейдите по пути ниже:

ios > Runner.xcodeproj > project.pbxproj

Откройте файл project.pbxproj и найдите PRODUCT_BUNDLE_IDENTIFIER.

Создать хранилище в Appwrite

После настройки нашего проекта нам нужно создать хранилище приложений для сохранения наших изображений на Backblaze. Для этого нам нужно перейти на вкладку Storage, нажать кнопку Create Bucket, ввести имя image_storage и нажать Create.

Наконец, мы должны обновить разрешение на хранение, поскольку нам нужно использовать его в нашем приложении Flutter. Для этого нам нужно перейти на вкладку Settings, прокрутить до раздела Execute Access, выбрать Any, проверить все разрешения и Update.

Appwrite + Backblaze во Flutter

После этого мы можем приступить к созданию нашего приложения. Чтобы начать, нам нужно клонировать проект, перейдя в нужный каталог и выполнив команду ниже:

git clone https://github.com/Mr-Malomz/flutter_backblaze.git

Запуск проекта

Во-первых, нам нужно установить зависимости проекта, выполнив команду ниже:

flutter pub get

Затем запустите проект с помощью следующей команды

flutter run

Приведенная выше команда запустит приложение на выбранном устройстве.

Интеграция и логика приложения

Для начала нам нужно создать класс для хранения наших учетных данных Appwrite и модель для преобразования ответа, отправленного из Appwrite, в объект Dart. Для этого нам нужно создать файл utils.dart в папке lib и добавить приведенный ниже фрагмент:

class AppConstant {
  final String projectId = "PROJECT ID GOES HERE";
  final String bucketId = "BUCKET ID GOES HERE";
  final String endpoint = "http://<MACBOOK IP GOES HERE>/v1";
}

class ImageModel {
  String $id;
  String bucketId;
  ImageModel({
    required this.$id,
    required this.bucketId,
  });
  factory ImageModel.fromJson(Map<dynamic, dynamic> json) {
    return ImageModel($id: json['\$id'], bucketId: json['bucketId']);
  }
}

Что касается свойства endpoint, нам необходимо изменить его для работы с адресом локальной сети нашей системы. Мы можем получить IP-адрес нашего MacBook, перейдя в раздел Network, как показано ниже:

Наконец, нам нужно создать служебный файл, чтобы отделить основную логику приложения от пользовательского интерфейса. Для этого нам нужно создать файл image_service.dart в той же папке lib и добавить фрагмент ниже:

import 'package:appwrite/appwrite.dart';
import 'package:flutter_backblaze/utils.dart';
import 'package:image_picker/image_picker.dart';

class ImageService {
  Client _client = Client();
  late Storage _storage;
  final _appConstant = AppConstant();

  ImageService() {
    _init();
  }

  //initialize the application
  _init() async {
    _client
        .setEndpoint(_appConstant.endpoint)
        .setProject(_appConstant.projectId);
    _storage = Storage(_client);
    //get current session
    Account account = Account(_client);
    try {
      await account.get();
    } on AppwriteException catch (e) {
      if (e.code == 401) {
        account
            .createAnonymousSession()
            .then((value) => value)
            .catchError((e) => e);
      }
    }
  }

  Future<List<ImageModel>> getImages() async {
    try {
      var data = await _storage.listFiles(bucketId: _appConstant.bucketId);
      var imageList = data.files
          .map((doc) => ImageModel($id: doc.$id, bucketId: doc.bucketId))
          .toList();
      return imageList;
    } catch (e) {
      throw Exception('Error getting list of images');
    }
  }

  Future saveImage(XFile file) async {
    try {
      var data = await _storage.createFile(
        bucketId: _appConstant.bucketId,
        fileId: ID.unique(),
        file: InputFile.fromPath(path: file.path, filename: file.name),
      );
      return data;
    } catch (e) {
      throw Exception('Error saving image');
    }
  }

  Future getImagePreview(String id) async {
    try {
      var data =
          _storage.getFilePreview(bucketId: _appConstant.bucketId, fileId: id);
      return data;
    } catch (e) {
      throw Exception('Error getting image preview');
    }
  }
}

Приведенный выше фрагмент делает следующее:

  • Импортирует необходимые зависимости
  • Создает класс AuthService со свойствами _client и _storage для подключения к экземпляру Appwrite.
  • Создает метод _init, который настраивает Appwrite, используя свойства
  • Создает методы getImages, saveImage и getImagePreview, которые используют свойство _storage для получения, сохранения и предварительного просмотра изображений.

Использование сервиса

Во-первых, нам нужно изменить файл image_screen.dart внутри папки screens, импортировав необходимые зависимости и используя сервис для выполнения необходимых операций:\

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_backblaze/image_service.dart';
import 'package:flutter_backblaze/utils.dart';
import 'package:image_picker/image_picker.dart';

class ImageScreen extends StatefulWidget {
  const ImageScreen({super.key});

  @override
  State<ImageScreen> createState() => _ImageScreenState();
}

class _ImageScreenState extends State<ImageScreen> {
  late XFile file;
  late List<ImageModel> _images;
  var _service = ImageService();
  bool _isLoading = false;
  bool _isError = false;

  @override
  void initState() {
    _getImageList();
    super.initState();
  }

  _getImageList() {
    setState(() {
      _isLoading = true;
    });
    _service
        .getImages()
        .then((value) => {
              setState(() {
                _isLoading = false;
                _images = value;
              })
            })
        .catchError((_) {
      setState(() {
        _isLoading = false;
        _isError = true;
      });
    });
  }

  _saveImage(XFile selectedFile) {
    _service.saveImage(selectedFile).then((value) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Image uploaded successfully!')),
      );
      _getImageList();
    }).catchError((_) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error uploading image!')),
      );
    });
  }

  Future _pickImage() async {
    try {
      final image = await ImagePicker().pickImage(source: ImageSource.gallery);
      if (image == null) return;
      _saveImage(image);
    } on PlatformException catch (e) {
      throw Exception(e);
    }
  }

  Widget build(BuildContext context) {
    //UI code goes here
  }
}

Приведенный выше фрагмент делает следующее:

  • Строка 1–5: импортирует необходимые зависимости.
  • Строки 15–19. Создает свойства file, _images, _service, _isLoading и _isError для управления состоянием приложения и другими необходимыми службами.
  • Строка 21-68: Создает методы _getImageList, _saveImage и _pickImage, которые используют _service для получения списка изображений и сохранения изображения.

Наконец, нам нужно обновить пользовательский интерфейс, чтобы использовать созданные свойства для условного отображения списка изображений, добавить предварительный просмотр с помощью сервиса _service.getImagePreview, а также вызвать метод _pickImage для загрузки изображения при нажатии.

//import goes here

class ImageScreen extends StatefulWidget {
  const ImageScreen({super.key});

  @override
  State<ImageScreen> createState() => _ImageScreenState();
}

class _ImageScreenState extends State<ImageScreen> {
  //properties goes here

  @override
  void initState() {
    _getImageList();
    super.initState();
  }

  //methods goes here

  Widget build(BuildContext context) {
    return _isLoading
        ? const Center(
            child: CircularProgressIndicator(
            color: Colors.black,
          ))
        : _isError
            ? const Center(
                child: Text(
                  'Error getting images',
                  style: TextStyle(
                    color: Colors.red,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              )
            : Scaffold(
                appBar: AppBar(
                  title: const Text('Appwrite + Backblaze'),
                  backgroundColor: Colors.black,
                ),
                body: _images.isNotEmpty
                    ? ListView.builder(
                        itemCount: _images.length,
                        itemBuilder: (context, index) {
                          return Container(
                            decoration: const BoxDecoration(
                              border: Border(
                                bottom:
                                    BorderSide(width: .5, color: Colors.grey),
                              ),
                            ),
                            padding: const EdgeInsets.fromLTRB(10, 20, 10, 20),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              children: [
                                Expanded(
                                  flex: 7,
                                  child: Row(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.center,
                                    children: [
                                      Container(
                                        height: 45,
                                        width: 45,
                                        child: FutureBuilder(
                                          future: _service.getImagePreview(
                                              _images[index].$id),
                                          builder: (context, snapshot) {
                                            return snapshot.hasData &&
                                                    snapshot.data != null
                                                ? Image.memory(
                                                    snapshot.data,
                                                  )
                                                : const CircularProgressIndicator();
                                          },
                                        ),
                                      ),
                                      const SizedBox(width: 10.0),
                                      Text(
                                        _images[index].$id,
                                      )
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          );
                        },
                      )
                    : const Center(
                        child: Text(
                          'No images uploaded yet. Click "+" button to upload',
                          style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                floatingActionButton: FloatingActionButton(
                  onPressed: () {
                    _pickImage();
                  },
                  tooltip: 'upload image',
                  child: const Icon(Icons.add),
                  backgroundColor: Colors.black,
                ),
              );
  }
}

После этого мы перезапускаем приложение с помощью редактора кода или запускаем команду ниже:

flutter run

Мы также можем подтвердить загруженные изображения на вкладках Storage Appwrite и Browse File в Backblaze.

Заключение

В этом посте обсуждалось, как добавить адаптер Backblaze в проект Appwrite. Помимо того, что обсуждалось выше, Appwrite также поддерживает другие адаптеры, такие как Amazon S3, Dospaces, Linode и Wasabi. Эти ресурсы также могут быть полезны:

Источник:

#Flutter
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу