Как добавить адаптер Backblaze к экземпляру Appwrite
Одной из наиболее важных особенностей Appwrite является его гибкость. Он предоставляет надежные инструменты, которые малые и крупные предприятия могут использовать для настройки серверной части с нуля или постепенного внедрения ее функций в существующую инфраструктуру.
В этом посте мы рассмотрим гибкость Appwrite за счет использования Appwrite Storage для управления файлами. Мы достигнем этого, создав приложение Flutter, которое использует адаптер Appwrite и Backblaze для управления изображениями. Репозиторий проекта можно найти здесь.
Обзор технологий
Backblaze — компания-разработчик программного обеспечения, которая обеспечивает онлайн-резервное копирование и управление хранением данных для изображений, видео, PDF-файлов и других типов файлов.
Помимо стандартной поддержки Appwrite для хранения файлов в локальном хранилище сервера, он также предоставляет адаптеры для плавной интеграции внешнего хранилища, такого как 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. Эти ресурсы также могут быть полезны: