1
+ #!/usr/bin/env python
2
+ # filepath: /home/runner/work/template-fastapi/template-fastapi/scripts/files.py
3
+
4
+ import os
5
+ from pathlib import Path
6
+ from typing import List , Optional
7
+
8
+ import typer
9
+ from rich .console import Console
10
+ from rich .table import Table
11
+
12
+ from template_fastapi .repositories .files import FileRepository
13
+
14
+ app = typer .Typer ()
15
+ console = Console ()
16
+ file_repo = FileRepository ()
17
+
18
+
19
+ @app .command ()
20
+ def list_files (
21
+ prefix : Optional [str ] = typer .Option (None , "--prefix" , "-p" , help = "ファイル名のプレフィックス" ),
22
+ ):
23
+ """ファイル一覧を取得する"""
24
+ console .print (f"[bold green]ファイル一覧[/bold green]を取得します" )
25
+
26
+ if prefix :
27
+ console .print (f"プレフィックス: { prefix } " )
28
+
29
+ try :
30
+ files = file_repo .list_files (prefix = prefix )
31
+
32
+ if not files :
33
+ console .print ("[yellow]ファイルが見つかりませんでした[/yellow]" )
34
+ return
35
+
36
+ # テーブルで表示
37
+ table = Table (title = "ファイル一覧" )
38
+ table .add_column ("ファイル名" , style = "cyan" )
39
+ table .add_column ("サイズ (bytes)" , style = "green" )
40
+ table .add_column ("コンテンツタイプ" , style = "yellow" )
41
+ table .add_column ("最終更新日時" , style = "magenta" )
42
+
43
+ for file in files :
44
+ table .add_row (
45
+ file .name ,
46
+ str (file .size ) if file .size else "N/A" ,
47
+ file .content_type or "N/A" ,
48
+ file .last_modified .strftime ("%Y-%m-%d %H:%M:%S" ) if file .last_modified else "N/A"
49
+ )
50
+
51
+ console .print (table )
52
+ console .print (f"[bold blue]合計: { len (files )} 件[/bold blue]" )
53
+
54
+ except Exception as e :
55
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
56
+
57
+
58
+ @app .command ()
59
+ def upload_file (
60
+ file_path : str = typer .Argument (..., help = "アップロードするファイルのパス" ),
61
+ blob_name : Optional [str ] = typer .Option (None , "--name" , "-n" , help = "Blob名(指定しない場合はファイル名を使用)" ),
62
+ ):
63
+ """単一のファイルをアップロードする"""
64
+ file_path_obj = Path (file_path )
65
+
66
+ if not file_path_obj .exists ():
67
+ console .print (f"[bold red]エラー[/bold red]: ファイル '{ file_path } ' が見つかりません" )
68
+ return
69
+
70
+ if not file_path_obj .is_file ():
71
+ console .print (f"[bold red]エラー[/bold red]: '{ file_path } ' はファイルではありません" )
72
+ return
73
+
74
+ blob_name = blob_name or file_path_obj .name
75
+ console .print (f"[bold green]ファイル[/bold green]: { file_path } -> { blob_name } " )
76
+
77
+ try :
78
+ with open (file_path_obj , "rb" ) as f :
79
+ file_data = f .read ()
80
+
81
+ uploaded_file = file_repo .upload_file (
82
+ file_name = blob_name ,
83
+ file_data = file_data ,
84
+ content_type = None # Let Azure detect the content type
85
+ )
86
+
87
+ console .print (f"[bold green]アップロード成功[/bold green]" )
88
+ console .print (f" ファイル名: { uploaded_file .name } " )
89
+ console .print (f" サイズ: { uploaded_file .size } bytes" )
90
+ console .print (f" コンテンツタイプ: { uploaded_file .content_type } " )
91
+ console .print (f" URL: { uploaded_file .url } " )
92
+
93
+ except Exception as e :
94
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
95
+
96
+
97
+ @app .command ()
98
+ def upload_multiple_files (
99
+ file_paths : List [str ] = typer .Argument (..., help = "アップロードするファイルのパス(複数指定可能)" ),
100
+ ):
101
+ """複数のファイルを同時にアップロードする"""
102
+ console .print (f"[bold green]複数ファイル[/bold green]をアップロードします" )
103
+
104
+ # ファイルの存在チェック
105
+ valid_files = []
106
+ for file_path in file_paths :
107
+ file_path_obj = Path (file_path )
108
+ if not file_path_obj .exists ():
109
+ console .print (f"[bold red]警告[/bold red]: ファイル '{ file_path } ' が見つかりません - スキップします" )
110
+ continue
111
+ if not file_path_obj .is_file ():
112
+ console .print (f"[bold red]警告[/bold red]: '{ file_path } ' はファイルではありません - スキップします" )
113
+ continue
114
+ valid_files .append (file_path_obj )
115
+
116
+ if not valid_files :
117
+ console .print ("[bold red]エラー[/bold red]: 有効なファイルが見つかりませんでした" )
118
+ return
119
+
120
+ try :
121
+ file_data_list = []
122
+ for file_path_obj in valid_files :
123
+ with open (file_path_obj , "rb" ) as f :
124
+ file_data = f .read ()
125
+ file_data_list .append ((file_path_obj .name , file_data , None ))
126
+
127
+ uploaded_files = file_repo .upload_files (file_data_list )
128
+
129
+ console .print (f"[bold green]アップロード成功[/bold green]: { len (uploaded_files )} 件" )
130
+ for uploaded_file in uploaded_files :
131
+ console .print (f" - { uploaded_file .name } ({ uploaded_file .size } bytes)" )
132
+
133
+ except Exception as e :
134
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
135
+
136
+
137
+ @app .command ()
138
+ def download_file (
139
+ blob_name : str = typer .Argument (..., help = "ダウンロードするBlobの名前" ),
140
+ output_path : Optional [str ] = typer .Option (None , "--output" , "-o" , help = "出力ファイルのパス" ),
141
+ ):
142
+ """ファイルをダウンロードする"""
143
+ console .print (f"[bold green]ファイル[/bold green]: { blob_name } をダウンロードします" )
144
+
145
+ try :
146
+ file_data = file_repo .download_file (blob_name )
147
+
148
+ output_path = output_path or blob_name
149
+ output_path_obj = Path (output_path )
150
+
151
+ with open (output_path_obj , "wb" ) as f :
152
+ f .write (file_data )
153
+
154
+ console .print (f"[bold green]ダウンロード成功[/bold green]" )
155
+ console .print (f" 出力先: { output_path_obj .absolute ()} " )
156
+ console .print (f" サイズ: { len (file_data )} bytes" )
157
+
158
+ except Exception as e :
159
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
160
+
161
+
162
+ @app .command ()
163
+ def get_file_info (
164
+ blob_name : str = typer .Argument (..., help = "情報を取得するBlobの名前" ),
165
+ ):
166
+ """ファイル情報を取得する"""
167
+ console .print (f"[bold green]ファイル情報[/bold green]: { blob_name } " )
168
+
169
+ try :
170
+ file_info = file_repo .get_file_info (blob_name )
171
+
172
+ console .print (f" ファイル名: { file_info .name } " )
173
+ console .print (f" サイズ: { file_info .size } bytes" )
174
+ console .print (f" コンテンツタイプ: { file_info .content_type } " )
175
+ console .print (f" 最終更新日時: { file_info .last_modified } " )
176
+ console .print (f" URL: { file_info .url } " )
177
+
178
+ except Exception as e :
179
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
180
+
181
+
182
+ @app .command ()
183
+ def delete_file (
184
+ blob_name : str = typer .Argument (..., help = "削除するBlobの名前" ),
185
+ force : bool = typer .Option (False , "--force" , "-f" , help = "確認なしで削除する" ),
186
+ ):
187
+ """ファイルを削除する"""
188
+ console .print (f"[bold green]ファイル削除[/bold green]: { blob_name } " )
189
+
190
+ try :
191
+ # 削除前に確認
192
+ if not force :
193
+ file_info = file_repo .get_file_info (blob_name )
194
+ console .print ("以下のファイルを削除しようとしています:" )
195
+ console .print (f" ファイル名: { file_info .name } " )
196
+ console .print (f" サイズ: { file_info .size } bytes" )
197
+ console .print (f" 最終更新日時: { file_info .last_modified } " )
198
+
199
+ if not typer .confirm ("削除してもよろしいですか?" ):
200
+ console .print ("[yellow]削除をキャンセルしました[/yellow]" )
201
+ return
202
+
203
+ file_repo .delete_file (blob_name )
204
+ console .print (f"[bold green]削除成功[/bold green]: { blob_name } " )
205
+
206
+ except Exception as e :
207
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
208
+
209
+
210
+ @app .command ()
211
+ def delete_multiple_files (
212
+ blob_names : List [str ] = typer .Argument (..., help = "削除するBlobの名前(複数指定可能)" ),
213
+ force : bool = typer .Option (False , "--force" , "-f" , help = "確認なしで削除する" ),
214
+ ):
215
+ """複数のファイルを同時に削除する"""
216
+ console .print (f"[bold green]複数ファイル削除[/bold green]: { len (blob_names )} 件" )
217
+
218
+ try :
219
+ # 削除前に確認
220
+ if not force :
221
+ console .print ("以下のファイルを削除しようとしています:" )
222
+ for blob_name in blob_names :
223
+ console .print (f" - { blob_name } " )
224
+
225
+ if not typer .confirm ("削除してもよろしいですか?" ):
226
+ console .print ("[yellow]削除をキャンセルしました[/yellow]" )
227
+ return
228
+
229
+ deleted_files = file_repo .delete_files (blob_names )
230
+ console .print (f"[bold green]削除成功[/bold green]: { len (deleted_files )} 件" )
231
+ for deleted_file in deleted_files :
232
+ console .print (f" - { deleted_file } " )
233
+
234
+ except Exception as e :
235
+ console .print (f"[bold red]エラー[/bold red]: { str (e )} " )
236
+
237
+
238
+ if __name__ == "__main__" :
239
+ app ()
0 commit comments