Balbas Code

Dartで動的な画像を差分更新する_その2

公開日: 2024-07-24 23:04:21

前回のDartでの画像の差分更新の記事では、マッピングファイルを手動で更新。
という流れとなっていたのですが、手動更新はやっぱりスペルミス等のことを考えると怖い。
→自動でできないか?と考えたらサーバーの同じ階層にPHPファイルを配置して、そちらのURLにアクセスしてPHPファイル経由でフォルダ内の画像ファイル名を取得するようにできないかを考えましたら、、、

できました‪\(^o^)/‬

前回作成していたJSONファイル
files.json (手動更新)


{
"files": [
"testImage1.png",
"testImage2.png",
"testImage3.png",
"testImage4.png",
"testImage5.png",
"testImage6.png",
"testImage7.png",
"testImage8.png",
"testImage9.png",
"testImage10.png",
"newImage1.png",
"newImage2.png"
]
}



今回作成のPHPファイル

get_files.php


<?php
// 画像ファイルのディレクトリを指定
$directory = __DIR__;

// 画像ファイルの拡張子をリスト
$image_extensions = ['jpg', 'jpeg', 'png', 'gif'];

// ディレクトリ内のファイルをスキャン
$files = scandir($directory);

// 画像ファイルをフィルタリング
$image_files = array_filter($files, function ($file) use ($directory, $image_extensions) {
$file_path = $directory . DIRECTORY_SEPARATOR . $file;
$extension = pathinfo($file_path, PATHINFO_EXTENSION);
return is_file($file_path) && in_array(strtolower($extension), $image_extensions);
});

// JSONレスポンスを作成
$response = [
'files' => array_values($image_files)
];

// ヘッダーを設定してJSONレスポンスを返す
header('Content-Type: application/json');
echo json_encode($response);
?>


URLにアクセスすると同じ結果が返ります。


前回書いた画像の差分更新のコードを一部修正

main.dart


import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'dart:convert';

void main() {
// プラグインの初期化
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Downloader',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
// ダウンロードした画像ファイルのリスト
List<File> _downloadedImages = [];

@override
void initState() {
super.initState();
// ダウンロードした画像を読み込む
_loadDownloadedImages();
}

// アプリケーションのドキュメントディレクトリ内に "images" サブディレクトリを作成し、そのディレクトリへのパスを返す
Future<Directory> _getImagesDirectory() async {
final directory = await getApplicationDocumentsDirectory();
final imagesDirectory = Directory(path.join(directory.path, 'images'));

if (!await imagesDirectory.exists()) {
await imagesDirectory.create(recursive: true);
}

return imagesDirectory;
}

// "images" サブディレクトリ内のファイルを読み込み、画像ファイルのみをリストに追加する
Future<void> _loadDownloadedImages() async {
final imagesDirectory = await _getImagesDirectory();
final List<FileSystemEntity> files = imagesDirectory.listSync();

setState(() {
_downloadedImages = files
.where((file) => file is File && _isImageFile(file.path))
.map((file) => File(file.path))
.toList();
});
}

// ファイルが画像ファイルかどうかを判定する
bool _isImageFile(String filePath) {
final extension = path.extension(filePath).toLowerCase();
return ['.jpg', '.jpeg', '.png', '.gif'].contains(extension);
}

// サーバーからファイルリストを取得し、ローカルの "images" サブディレクトリに存在しないファイルをダウンロードし、存在しないファイルを削除する
Future<void> syncImagesWithServer(String baseUrl, String jsonFileUrl) async {
try {
// サーバーからファイルリストを取得
final response = await http.get(Uri.parse(jsonFileUrl));
if (response.statusCode == 200) {
final List<dynamic> fileList = json.decode(response.body)['files'];
final imagesDirectory = await _getImagesDirectory();

// ローカルファイルとサーバーのファイルリストを比較
final localFiles = imagesDirectory
.listSync()
.where((file) => file is File && _isImageFile(file.path))
.map((file) => path.basename(file.path))
.toList();

// サーバー上に存在しないローカルファイルを削除
for (String localFile in localFiles) {
if (!fileList.contains(localFile)) {
final filePath = path.join(imagesDirectory.path, localFile);
final file = File(filePath);
if (await file.exists()) {
await file.delete();
print('Deleted local file: $filePath');
}
}
}

// サーバー上の新しいファイルをダウンロード
for (String fileName in fileList) {
final filePath = path.join(imagesDirectory.path, fileName);
final file = File(filePath);
if (!file.existsSync()) {
final fileUrl = '$baseUrl/$fileName';
await downloadAndSaveImage(fileUrl, filePath);
}
}

// ダウンロードされた画像のリストを更新
_loadDownloadedImages();
} else {
print('Failed to load file list');
}
} catch (e) {
print('Error: $e');
}
}

// 画像を指定されたパスに保存する
Future<void> downloadAndSaveImage(String imageUrl, String filePath) async {
try {
final response = await http.get(Uri.parse(imageUrl));
if (response.statusCode == 200) {
final file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
print('Image saved to $filePath');
} else {
print('Failed to download image');
}
} catch (e) {
print('Error: $e');
}
}

@override
Widget build(BuildContext context) {
// 画像ファイルが保存されているベースURL
final baseUrl = 'https://www.testfolder';
// サーバー上のファイルリストを返すPHPファイルのURL
final jsonFileUrl = 'https://www.testfolder/get_files.php';

return Scaffold(
appBar: AppBar(
title: Text('Image Downloader'),
),
body: Column(
children: <Widget>[
// サーバーと同期ボタン
ElevatedButton(
onPressed: () => syncImagesWithServer(baseUrl, jsonFileUrl),
child: Text('Sync Images with Server'),
),
// ダウンロードされた画像を表示するリストビュー
Expanded(
child: _downloadedImages.isNotEmpty
? ListView.builder(
itemCount: _downloadedImages.length,
itemBuilder: (context, index) {
return Image.file(_downloadedImages[index]);
},
)
: Center(child: Text('No images downloaded.')),
),
],
),
);
}
}

 


この部分だけPHPファイルを参照するように変更しました。


    // 画像ファイルが保存されているベースURL
final baseUrl = 'https://www.testfolder';
// サーバー上のファイルリストを返すPHPファイルのURL
final jsonFileUrl = 'https://www.testfolder/get_files.php';


サーバー内の画像と同じ階層に配置することで、そのフォルダ内の画像ファイル名を取得できます。
get_files

本日はマッピング機能を自動で作成する機能について勉強できました。
こちらの応用はエクスプローラーでサーバーを見ている時、画像やExcelデータ等を追加したら、常にこちらのような差分更新のマッピング機能が働いているんだとろうな!と勉強になりました。