echoss-storage 1.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: echoss-storage
3
+ Version: 1.1.4
4
+ Summary: echoss AI Bigdata Solution - Object Storage like S3 handler
5
+ Home-page:
6
+ Author: ckkim
7
+ Author-email: ckkim@12cm.co.kr
8
+ Keywords: echoss,object storage,s3handler,s3_handler
9
+ Requires-Python: >3.7
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: boto3~=1.19.0
12
+ Requires-Dist: opencv-python==4.8.0.74
13
+ Requires-Dist: numpy>=1.22.3
14
+ Requires-Dist: tqdm==4.63.2
15
+ Requires-Dist: pillow==10.1.0
16
+ Requires-Dist: echoss-fileformat>=1.1.0
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: keywords
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # v1.1.3 change package name from echoss_s3handler to echoss_storage and use echoss-fileformat
27
+ # v1.1.4 remove S3ClientHandler, add S3ResourceFileView class, change internal implementation
28
+ - initial config: str or dict
29
+ - add internal class S3ResouceFileView as return object get_object_file()
30
+ - move read_file() to S3ResouceFileView as_json() or as_text()
31
+ - move read_image() to S3ResouceFileView as_pil_image() or as as_cv2_image()
32
+
33
+ # install 방법
34
+ pip install -U echoss-storage
35
+
36
+ ## import 방법
37
+ ```python
38
+ from echoss_storage import S3ResourceHandler
39
+
40
+ ```
41
+
42
+ custom yolov7과 image_utils의 함수 사용시 S3에 있는 데이터를 사용하고자 하여 만든 Class
43
+
44
+ # Resource Class
45
+ 내부 주요 함수
46
+ ```
47
+ # s3 config 파일 읽기
48
+ s3_config = "../s3 config file path/ai_solution_dataset_test.yaml"
49
+ # class 할당
50
+ s3_resource = S3ResourceHandler(config, env=None)
51
+
52
+ s3_resource.get_bucket_list(data_info=False)
53
+ - 현재 config 파일에 해당하는 접속 계정에 있는 bucket들의 정보를 출력해준다.(버킷명, 버킷 생성날짜 등)
54
+ - data_info를 True로 하게 되면 출력이 버킷명이 key값, 마지막 생성일자를 vlaue로 하는 Dict형태로 출력되며,
55
+ false로 하면 버킷의 목록만 list형태로 출력.
56
+
57
+ s3_resource.get_object_info(bucket, file_path)
58
+ - parameter로 입력된 object의 마지막 수정일시(YYYY-MM-DD HH:MM:SS 꼴)와 s3link를 출력해준다.
59
+
60
+ s3_resource.get_object_list(bucket, s3_prefix, after_ts=0, pattern=None, tqdm_=True)
61
+ - s3_prefix는 버킷 이후 공통되는 경로 입력
62
+ - after_ts는 입력된 이후 추가/수정된 데이터만 추리기 위함(포맷 : "2023-02-28 00:00:00")
63
+ - patter은 확장자와 같은 부분
64
+ - object들의 path를 list로 반환한다.
65
+
66
+ s3_resource.download_object(bucket_name, target_file_path, download_file_path)
67
+ - S3에 있는 object를 local에 다운로드하기 위한 함수
68
+ - target_file_path는 다운로드 하고자하는 버킷 내의 object 경로
69
+ - download_file_path는 로컬에 다운받고자 하는 경로 입력(파일의 이름 포함)
70
+
71
+ s3_resource.upload_object(bucket_name, target_file_path, upload_file_path, ExtraArgs=None)
72
+ - locla에 있는 데이터를 S3에 업로드하기 위한 함수
73
+ - target_file_path는 업로드 하고자하는 로컬의 파일 경로
74
+ - upload_file_path는 s3에 업로드 하고자하는 경로
75
+ - ExtraArgs는 metadata 예를들어 {'ContentType': "video/mp4", 'ACL': "public-read"}위와 같이 입력
76
+
77
+ s3_resource.put_object(object_body, object_name, trg_bucket)
78
+ - 메모리상으로 저장되어있는 json혹은 txt 데이터를 S3 object로 업로드하는데 사용하는 함수
79
+ - object_body는 메모리 상으로 저장되어 있는 데이터
80
+ - object_name은 업로드 하고자하는 경로및 파일명의 path
81
+ - trg_bucket은 업로드 하고자하는 버킷명
82
+ - 이미지도 업로드할 수 있으나 이때에는 아래와 같이 이미지를 PIL, io라이브러리를 이용하여 가공한 뒤 업로드를 해야합니다.
83
+ img0 = Image.fromarray(im0, mode='RGB')
84
+ out_img = io.BytesIO()
85
+ img0.save(out_img, format='png')
86
+ out_img.seek(0)
87
+
88
+ s3_resource.s3tos3_put_object(src_file_name, trg_file_name, trg_s3_config, fin_print=True)
89
+ - 서로 다른 S3계정간의 데이터를 local에 따로 다운로드 한 뒤 업로드 하는 것이 아닌 메모리상으로 불러 업로드하는 함수
90
+ - src_file_name은 옮기고자하는 s3 파일의 전체 경로(버킷명은 안적어주어도 무방 -> class를 호출할때의 config파일에서 가지고 옴)
91
+ - trg_file_name은 옮겨가야할 s3에서의 경로 입력(파일명까지)
92
+ - trg_s3_config는 옮겨가야할 s3의 config파일(확장자는 yaml)
93
+
94
+ s3_resource.move_object(src_file_name, trg_file_name, bucket)
95
+ - 같은 s3내 같은 버킷에서의 파일 이동
96
+ - src_file_name은 원본 파일
97
+ - trg_file_name은 이동되어야 할 경로
98
+
99
+ s3_resource.read_file(bucket, file_name)
100
+ - S3에 있는 파일을 메모리로 읽기 위한 함수
101
+
102
+ s3_resource.read_image(bucket, file_name, library="cv2")
103
+ - S3에 있는 이미지 파일을 메모리로 읽는 함수
104
+ - 이후 사용되는 이미지 형식에 따라 옵션에 "cv2", "PIL"을 선택하여 사용 가능
105
+
106
+ ```
107
+
@@ -0,0 +1,82 @@
1
+ # v1.1.3 change package name from echoss_s3handler to echoss_storage and use echoss-fileformat
2
+ # v1.1.4 remove S3ClientHandler, add S3ResourceFileView class, change internal implementation
3
+ - initial config: str or dict
4
+ - add internal class S3ResouceFileView as return object get_object_file()
5
+ - move read_file() to S3ResouceFileView as_json() or as_text()
6
+ - move read_image() to S3ResouceFileView as_pil_image() or as as_cv2_image()
7
+
8
+ # install 방법
9
+ pip install -U echoss-storage
10
+
11
+ ## import 방법
12
+ ```python
13
+ from echoss_storage import S3ResourceHandler
14
+
15
+ ```
16
+
17
+ custom yolov7과 image_utils의 함수 사용시 S3에 있는 데이터를 사용하고자 하여 만든 Class
18
+
19
+ # Resource Class
20
+ 내부 주요 함수
21
+ ```
22
+ # s3 config 파일 읽기
23
+ s3_config = "../s3 config file path/ai_solution_dataset_test.yaml"
24
+ # class 할당
25
+ s3_resource = S3ResourceHandler(config, env=None)
26
+
27
+ s3_resource.get_bucket_list(data_info=False)
28
+ - 현재 config 파일에 해당하는 접속 계정에 있는 bucket들의 정보를 출력해준다.(버킷명, 버킷 생성날짜 등)
29
+ - data_info를 True로 하게 되면 출력이 버킷명이 key값, 마지막 생성일자를 vlaue로 하는 Dict형태로 출력되며,
30
+ false로 하면 버킷의 목록만 list형태로 출력.
31
+
32
+ s3_resource.get_object_info(bucket, file_path)
33
+ - parameter로 입력된 object의 마지막 수정일시(YYYY-MM-DD HH:MM:SS 꼴)와 s3link를 출력해준다.
34
+
35
+ s3_resource.get_object_list(bucket, s3_prefix, after_ts=0, pattern=None, tqdm_=True)
36
+ - s3_prefix는 버킷 이후 공통되는 경로 입력
37
+ - after_ts는 입력된 이후 추가/수정된 데이터만 추리기 위함(포맷 : "2023-02-28 00:00:00")
38
+ - patter은 확장자와 같은 부분
39
+ - object들의 path를 list로 반환한다.
40
+
41
+ s3_resource.download_object(bucket_name, target_file_path, download_file_path)
42
+ - S3에 있는 object를 local에 다운로드하기 위한 함수
43
+ - target_file_path는 다운로드 하고자하는 버킷 내의 object 경로
44
+ - download_file_path는 로컬에 다운받고자 하는 경로 입력(파일의 이름 포함)
45
+
46
+ s3_resource.upload_object(bucket_name, target_file_path, upload_file_path, ExtraArgs=None)
47
+ - locla에 있는 데이터를 S3에 업로드하기 위한 함수
48
+ - target_file_path는 업로드 하고자하는 로컬의 파일 경로
49
+ - upload_file_path는 s3에 업로드 하고자하는 경로
50
+ - ExtraArgs는 metadata 예를들어 {'ContentType': "video/mp4", 'ACL': "public-read"}위와 같이 입력
51
+
52
+ s3_resource.put_object(object_body, object_name, trg_bucket)
53
+ - 메모리상으로 저장되어있는 json혹은 txt 데이터를 S3 object로 업로드하는데 사용하는 함수
54
+ - object_body는 메모리 상으로 저장되어 있는 데이터
55
+ - object_name은 업로드 하고자하는 경로및 파일명의 path
56
+ - trg_bucket은 업로드 하고자하는 버킷명
57
+ - 이미지도 업로드할 수 있으나 이때에는 아래와 같이 이미지를 PIL, io라이브러리를 이용하여 가공한 뒤 업로드를 해야합니다.
58
+ img0 = Image.fromarray(im0, mode='RGB')
59
+ out_img = io.BytesIO()
60
+ img0.save(out_img, format='png')
61
+ out_img.seek(0)
62
+
63
+ s3_resource.s3tos3_put_object(src_file_name, trg_file_name, trg_s3_config, fin_print=True)
64
+ - 서로 다른 S3계정간의 데이터를 local에 따로 다운로드 한 뒤 업로드 하는 것이 아닌 메모리상으로 불러 업로드하는 함수
65
+ - src_file_name은 옮기고자하는 s3 파일의 전체 경로(버킷명은 안적어주어도 무방 -> class를 호출할때의 config파일에서 가지고 옴)
66
+ - trg_file_name은 옮겨가야할 s3에서의 경로 입력(파일명까지)
67
+ - trg_s3_config는 옮겨가야할 s3의 config파일(확장자는 yaml)
68
+
69
+ s3_resource.move_object(src_file_name, trg_file_name, bucket)
70
+ - 같은 s3내 같은 버킷에서의 파일 이동
71
+ - src_file_name은 원본 파일
72
+ - trg_file_name은 이동되어야 할 경로
73
+
74
+ s3_resource.read_file(bucket, file_name)
75
+ - S3에 있는 파일을 메모리로 읽기 위한 함수
76
+
77
+ s3_resource.read_image(bucket, file_name, library="cv2")
78
+ - S3에 있는 이미지 파일을 메모리로 읽는 함수
79
+ - 이후 사용되는 이미지 형식에 따라 옵션에 "cv2", "PIL"을 선택하여 사용 가능
80
+
81
+ ```
82
+
@@ -0,0 +1,5 @@
1
+ # init
2
+
3
+ __version__ = '1.1.3'
4
+
5
+ from .s3_handler import S3ResourceHandler
@@ -0,0 +1,376 @@
1
+ import io
2
+ import os
3
+ import boto3
4
+ import botocore.response
5
+ import cv2
6
+ import json
7
+ import numpy as np
8
+ import time
9
+ import datetime
10
+ import tqdm
11
+ from PIL import Image
12
+ from typing import Union
13
+
14
+ from echoss_fileformat import FileUtil, get_logger
15
+
16
+ logger = get_logger(logger_name='echoss_storage')
17
+
18
+ def _split_s3_key(s3_key):
19
+ key = str(s3_key)
20
+ last_name = key.split('/')[-1]
21
+ return key.replace(last_name, ""), last_name
22
+
23
+
24
+ # 빈문자열 체크
25
+ def _is_blank(str_data:str):
26
+ if str_data and str_data.strip():
27
+ return False
28
+ return True
29
+
30
+
31
+ # print시 문자에 색을 넣을 수 있는 함수
32
+ def colorstr(*input_data):
33
+ # Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world')
34
+ *args, string = input_data if len(input_data) > 1 else ('blue', 'bold', input_data[0]) # color arguments, string
35
+ colors = {'black': '\033[30m', # basic colors
36
+ 'red': '\033[31m',
37
+ 'green': '\033[32m',
38
+ 'yellow': '\033[33m',
39
+ 'blue': '\033[34m',
40
+ 'magenta': '\033[35m',
41
+ 'cyan': '\033[36m',
42
+ 'white': '\033[37m',
43
+ 'bright_black': '\033[90m', # bright colors
44
+ 'bright_red': '\033[91m',
45
+ 'bright_green': '\033[92m',
46
+ 'bright_yellow': '\033[93m',
47
+ 'bright_blue': '\033[94m',
48
+ 'bright_magenta': '\033[95m',
49
+ 'bright_cyan': '\033[96m',
50
+ 'bright_white': '\033[97m',
51
+ 'end': '\033[0m', # misc
52
+ 'bold': '\033[1m',
53
+ 'underline': '\033[4m'}
54
+ return ''.join(colors[x] for x in args) + f'{string}' + colors['end']
55
+
56
+
57
+ class S3ResourceHandler:
58
+ def __init__(self, config_file:Union[str, dict], env:str=None):
59
+ """
60
+ :param config_file: yaml or json file_path or config dictionary
61
+ """
62
+ if type(config_file) == dict:
63
+ self.config_file = config_file
64
+ elif type(config_file) == str and os.path.exists(config_file):
65
+ self.config_file = FileUtil.dict_load(config_file)
66
+ else:
67
+ logger.debug('설정 파일을 dict형식 혹은 파일의 경로로 입력해주세요.')
68
+
69
+ if env is not None and env in self.config_file:
70
+ self.s3_config = self.config_file[env]
71
+ else:
72
+ self.s3_config = self.config_file
73
+
74
+ if 's3' in self.s3_config:
75
+ self.s3_config = self.s3_config['s3']
76
+
77
+ self.s3_session = boto3.Session(
78
+ region_name=self.config_file['region_name'],
79
+ aws_access_key_id=self.config_file['access_key_id'],
80
+ aws_secret_access_key=self.config_file['secret_access_key'])
81
+
82
+ self.s3 = self.s3_session.resource(service_name='s3', endpoint_url=self.config_file.get('endpoint_url'))
83
+
84
+ def get_bucket_list(self, include_date:bool=False):
85
+ """
86
+ S3ClientHandler class의 get_bucket_list 함수를 사용하며,
87
+ data_info 옵션을 사용하여
88
+ 단순히 버킷의 리스트만 출력하거나, 버킷의 생성날짜까지 확인할 수 있도록 한 함수
89
+ :param include_date: True or False
90
+ :return: If date_info is True -> Output : Dict,
91
+ False -> Output : List
92
+ """
93
+ bucket_list_info = self.s3.buckets.all()
94
+
95
+ if include_date:
96
+ result_dict = {
97
+ bucket_info.name: bucket_info.creation_date.strftime('%Y-%m-%d')
98
+ for bucket_info in bucket_list_info
99
+ }
100
+ return result_dict
101
+ else:
102
+ result_list = [
103
+ bucket_info.name
104
+ for bucket_info in bucket_list_info
105
+ ]
106
+ return result_list
107
+
108
+ def select_bucket(self, bucket):
109
+ """
110
+ s3에서 작업 하기 전 작업할 버킷을 선택하는 단계 독립적으로는 거의 사용하지 않음
111
+ 거의 아래의 함수들에 사용
112
+ """
113
+ s3bucket = self.s3.Bucket(bucket)
114
+ return s3bucket
115
+
116
+ def get_object_list(self, bucket, s3_prefix, after_ts: int or str=0, pattern=None, use_tqdm=True):
117
+ """
118
+ after_ts 에는 "2023-02-28 00:00:00" 포맷으로 작성해야 한다.
119
+ :param bucket: bucket name
120
+ :param s3_prefix: 버킷 이후 공통되는 경로
121
+ :param after_ts: 입력되는 시간 이후 수정된 데이터만 추리기 위한 파라미터 ex) "2023-02-28 00:00:00"
122
+ :param pattern: 예를들면 확장자
123
+ :param use_tqdm: 예를들면 확장자
124
+ :return:
125
+ """
126
+ s3bucket = self.s3.Bucket(bucket)
127
+ object_list = s3bucket.objects.filter(Prefix=s3_prefix)
128
+
129
+ filenames = []
130
+ count = 0
131
+ if use_tqdm:
132
+ iterator = tqdm.tqdm(object_list)
133
+ else:
134
+ iterator = object_list
135
+
136
+ for obj in iterator:
137
+ count += 1
138
+ if pattern is not None and not pattern in obj.key:
139
+ continue
140
+
141
+ last_modified_dt = obj.last_modified
142
+ s3_ts = last_modified_dt.timestamp() * 1000
143
+ if after_ts != 0:
144
+ after_ts_ = datetime.datetime.strptime(after_ts, "%Y-%m-%d %H:%M:%S")
145
+ after_ts__ = time.mktime(after_ts_.timetuple())*1000
146
+ else:
147
+ after_ts__ = after_ts
148
+ if s3_ts > after_ts__:
149
+ s3_path, s3_filename = _split_s3_key(obj.key)
150
+ # directory check
151
+ if _is_blank(s3_filename) or s3_filename.endswith("/"):
152
+ pass
153
+ else:
154
+ filenames.append(s3_path + s3_filename)
155
+ return filenames
156
+
157
+ def download_object(self, bucket_name, target_file_path, download_file_path):
158
+ """
159
+ 버킷내의 object를 로컬로 다운로드 하기 위한 함수
160
+ :param bucket_name: 버킷명
161
+ :param target_file_path: 다운로드 하고자하는 버킷 내의 object 이름(경로)
162
+ :param download_file_path: 로컬에 다운로드 받고자 하는 경로(파일의 이름 포함)
163
+ :return:
164
+ """
165
+ s3bucket = self.s3.Bucket(bucket_name)
166
+ s3bucket.download_file(target_file_path, download_file_path)
167
+
168
+ def upload_object(self, bucket_name, target_file_path, upload_file_path, ExtraArgs = None):
169
+ """
170
+ 로컬의 파일을 s3로 업로드 하기 위한 함수
171
+ :param bucket_name: 버킷명
172
+ :param target_file_path: 업로드 하고자하는 로칼의 파일 경로(파일 이름 포함)
173
+ :param upload_file_path: S3에 업로드 하고 싶은 경로(폴더 구조와 파일의 이름 포함)
174
+ :param ExtraArgs: metadata 입력 ex. {'ContentType': "video/mp4", 'ACL': "public-read"}
175
+ :return:
176
+ """
177
+ s3bucket = self.s3.Bucket(bucket_name)
178
+ if ExtraArgs is None:
179
+ s3bucket.upload_file(target_file_path, upload_file_path)
180
+ else:
181
+ s3bucket.upload_file(target_file_path, upload_file_path, ExtraArgs)
182
+
183
+ def put_object(self, object_body, object_name, trg_bucket):
184
+ """
185
+ 로컬의 파일을 s3로 업로드 하기 위한 함수
186
+ 이 함수는 json파일또는 txt파일을 업로드하는데 사용
187
+ (이미지 업로드에 사용할 수 있으나 이미지의 경우 입력 데이터를 잘 넣어주어야 함)
188
+ 이미지를 업로드 하고 싶다면 yolov7/utils/detect.py 파일 164번째 줄 참고
189
+ :param object_body: Object data
190
+ :param object_name: The full path to upload
191
+ :param trg_bucket: The bucket to upload
192
+ :return:
193
+ """
194
+ extention = os.path.splitext(os.path.basename(object_name))[-1]
195
+ if (extention == ".json") | (extention == "json"):
196
+ object_body = json.dumps(object_body, indent="\t", ensure_ascii=False)
197
+ trg_s3bucket = self.select_bucket(trg_bucket)
198
+ trg_s3bucket.put_object(Body=object_body, Key=object_name, ACL="public-read")
199
+
200
+ def s3tos3_put_object(self, src_file_name, trg_file_name, trg_s3_config, fin_print=True):
201
+ """
202
+ ex) src_s3 = S3ResourceHandler(src_s3_config)\n
203
+ src_s3.s3tos3_put_object(Put parameters)
204
+ :param src_file_name: File path
205
+ :param trg_file_name: Final file path
206
+ :param trg_s3_config: Target s3 config data, yaml data
207
+ :param fin_print:
208
+ :return: None
209
+ """
210
+ trg_s3 = S3ResourceHandler(trg_s3_config)
211
+
212
+ # with 문으로 안전하게 open
213
+ with self.get_object_file(self.config_file['bucket'], src_file_name) as src_file_view:
214
+ # 파일 확장자 검사
215
+ ext = os.path.splitext(src_file_name)[-1].lower()
216
+
217
+ # 확장자에 따라 읽기 방식 선택
218
+ if ext == '.json':
219
+ data = src_file_view.as_json() # dict 반환
220
+ else:
221
+ data = src_file_view.as_bytes() # bytes 반환
222
+
223
+ # 데이터 업로드
224
+ trg_s3.put_object(data, trg_file_name, trg_s3_config['bucket'])
225
+
226
+ if fin_print:
227
+ logger.debug(
228
+ f"{colorstr(self.config_file['bucket'])}: {src_file_name} {colorstr('red', 'bold', '->')} {colorstr(trg_s3_config['bucket'])}: {trg_file_name}"
229
+ )
230
+
231
+ def move_object(self, src_file_name, trg_file_name, bucket, acl='public-read'):
232
+ """
233
+ Args:
234
+ src_file_name(str) : 원본 파일 명(버킷명을 포함하지 않음) \n
235
+ trg_file_name(str) : 이동 대상 경로 파일 명(버킷명을 포함하지 않음) \n
236
+ bucket(str) : 버킷명 \n
237
+ acl (str) : ACL
238
+ Returns:
239
+ src_file들을 같은 버킷 내에서 이동
240
+ """
241
+ try:
242
+ copy_source = {'Bucket': bucket, 'Key': src_file_name}
243
+ self.s3.Object(bucket, trg_file_name).copy_from(CopySource=copy_source, ACL=acl)
244
+ self.s3.Object(bucket, src_file_name).delete()
245
+ except Exception as e:
246
+ logger.error(f"error moving object {src_file_name} to {trg_file_name}: {e}")
247
+ raise
248
+
249
+ def remove_empty_folder(self, bucket:str):
250
+ """
251
+ 버킷 내부 Temp 폴더 속 빈 폴더들을 자동으로 삭제시키는 함수
252
+ Args:
253
+ bucket (str) : 버킷명
254
+ Returns:
255
+ 빈 폴더들을 자동으로 삭제
256
+ """
257
+ s3_bucket = self.select_bucket(bucket)
258
+ for obj in s3_bucket.objects.filter(Prefix='Temp/',Delimiter='/*'):
259
+ # 폴더 객체가 없는 경우에만 삭제
260
+ if obj.key[-1] != '/':
261
+ continue
262
+ prefix = obj.key
263
+
264
+ # 해당 폴더의 객체 수를 세어 빈 폴더인지 확인
265
+ num_objects = sum(1 for _ in s3_bucket.objects.filter(Prefix=prefix))
266
+ if (num_objects > 0) and (prefix != 'Temp/'):
267
+ print('Delete empty folder -',prefix)
268
+ s3_bucket.objects.filter(Prefix=prefix).delete()
269
+ continue
270
+
271
+ def get_object_file(self, bucket:str, file_name):
272
+ s3_object = self.s3.Object(bucket, file_name)
273
+ return self.S3ResourceFileView(s3_object)
274
+
275
+ class S3ResourceFileView:
276
+ def __init__(self, s3_object):
277
+ self.obj = s3_object
278
+ self._body = None # Lazy Loading
279
+
280
+ def _load_body(self):
281
+ if self._body is None:
282
+ stream_body = self.obj.get()['Body']
283
+ if isinstance(stream_body, botocore.response.StreamingBody):
284
+ self._body = io.BytesIO(stream_body.read()) # 항상 BytesIO로 변환
285
+ else:
286
+ self._body = stream_body # 이미 BytesIO거나 안전한 경우
287
+ return self._body
288
+
289
+ def as_bytes(self):
290
+ body = self._load_body()
291
+ if body.seekable():
292
+ body.seek(0)
293
+ return body.read()
294
+
295
+ def as_text(self, encoding='utf-8'):
296
+ return self.as_bytes().decode(encoding)
297
+
298
+ def as_json(self):
299
+ return json.loads(self.as_text())
300
+
301
+ def as_pil_image(self):
302
+ return Image.open(self.as_bytes())
303
+
304
+ def as_cv2_image(self):
305
+ np_bytes = np.asarray(bytearray(self.as_bytes()), dtype=np.uint8)
306
+ return cv2.imdecode(np_bytes, cv2.IMREAD_COLOR)
307
+
308
+ def save_to_disk(self, filepath):
309
+ with open(filepath, 'wb') as f:
310
+ f.write(self.as_bytes())
311
+
312
+ def seek(self, pos, whence=0):
313
+ body = self._load_body()
314
+ return body.seek(pos, whence)
315
+
316
+ def tell(self):
317
+ body = self._load_body()
318
+ return body.tell()
319
+
320
+ def close(self):
321
+ if self._body:
322
+ self._body.close()
323
+ self._body = None
324
+
325
+ def __enter__(self):
326
+ # with 문 시작할 때 반환할 객체
327
+ return self
328
+
329
+ def __exit__(self, exc_type, exc_value, traceback):
330
+ # with 문 끝날 때 자동으로 close 호출
331
+ self.close()
332
+
333
+ def __getattr__(self, name):
334
+ """
335
+ Delegate any undefined attribute to the original S3.Object instance.
336
+ """
337
+ return getattr(self.obj, name)
338
+
339
+ def get_object_info(self, bucket, file_path):
340
+ """
341
+ object의 최종 수정일시와 s3_link를 output으로 출력한다.
342
+ 출력된 값을 DB에 적재할 수 있다.
343
+ UTC 기준이기 때문에 한국 시간(KST, UTC+9) 으로 변환하기 위해 9시간을 더하는 작업
344
+
345
+ :param bucket: 버킷 명
346
+ :param file_path: object path
347
+ :return: (last_modified, url)
348
+ """
349
+ last_modified = ""
350
+ url = None
351
+ with self.get_object_file(bucket, file_path) as object_info:
352
+ last_modified = last_modified = (object_info.last_modified + datetime.timedelta(hours=9)).strftime("%Y-%m-%d %H:%M:%S")
353
+ url = "%s/%s/%s" % (self.config_file['endpoint_url'], bucket, file_path)
354
+ return last_modified, url
355
+
356
+
357
+ if __name__ == '__main__':
358
+ data_dict = FileUtil.dict_load('../data/45_abalone_data/yolo_data/ai_solution_dataset_test.yaml')
359
+
360
+ s3resource = S3ResourceHandler(data_dict)
361
+ s3_bucket_list = s3resource.get_bucket_list()
362
+ print(s3_bucket_list)
363
+
364
+ # with open("../data/45_abalone_data/yolo_data/AI_Hub_s3_config.yaml") as f:
365
+ # aihub_data_dict = yaml.load(f, Loader=yaml.SafeLoader)
366
+ # objects_list = s3resource.get_object_list(data_dict['bucket'], "bb_seg_data")
367
+ # print(objects_list[0])
368
+ # new_name = f"113.패류 종자생산(전복) 데이터/02.저작도구/{os.path.basename(objects_list[0])}"
369
+ #
370
+ # s3resource.s3tos3_put_object(objects_list[0], new_name, data_dict, aihub_data_dict)
371
+ object_lists = s3resource.get_object_list(data_dict['bucket'], data_dict['yolo_data_path'], "2023-02-28 14:00:00")
372
+ print(len(object_lists))
373
+ object_lists = s3resource.get_object_list(data_dict['bucket'], data_dict['yolo_data_path'])
374
+ print(len(object_lists))
375
+ # ob_info = s3resource.get_object_info(data_dict['bucket'], object_lists[0])
376
+ # print(ob_info)
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: echoss-storage
3
+ Version: 1.1.4
4
+ Summary: echoss AI Bigdata Solution - Object Storage like S3 handler
5
+ Home-page:
6
+ Author: ckkim
7
+ Author-email: ckkim@12cm.co.kr
8
+ Keywords: echoss,object storage,s3handler,s3_handler
9
+ Requires-Python: >3.7
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: boto3~=1.19.0
12
+ Requires-Dist: opencv-python==4.8.0.74
13
+ Requires-Dist: numpy>=1.22.3
14
+ Requires-Dist: tqdm==4.63.2
15
+ Requires-Dist: pillow==10.1.0
16
+ Requires-Dist: echoss-fileformat>=1.1.0
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: keywords
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # v1.1.3 change package name from echoss_s3handler to echoss_storage and use echoss-fileformat
27
+ # v1.1.4 remove S3ClientHandler, add S3ResourceFileView class, change internal implementation
28
+ - initial config: str or dict
29
+ - add internal class S3ResouceFileView as return object get_object_file()
30
+ - move read_file() to S3ResouceFileView as_json() or as_text()
31
+ - move read_image() to S3ResouceFileView as_pil_image() or as as_cv2_image()
32
+
33
+ # install 방법
34
+ pip install -U echoss-storage
35
+
36
+ ## import 방법
37
+ ```python
38
+ from echoss_storage import S3ResourceHandler
39
+
40
+ ```
41
+
42
+ custom yolov7과 image_utils의 함수 사용시 S3에 있는 데이터를 사용하고자 하여 만든 Class
43
+
44
+ # Resource Class
45
+ 내부 주요 함수
46
+ ```
47
+ # s3 config 파일 읽기
48
+ s3_config = "../s3 config file path/ai_solution_dataset_test.yaml"
49
+ # class 할당
50
+ s3_resource = S3ResourceHandler(config, env=None)
51
+
52
+ s3_resource.get_bucket_list(data_info=False)
53
+ - 현재 config 파일에 해당하는 접속 계정에 있는 bucket들의 정보를 출력해준다.(버킷명, 버킷 생성날짜 등)
54
+ - data_info를 True로 하게 되면 출력이 버킷명이 key값, 마지막 생성일자를 vlaue로 하는 Dict형태로 출력되며,
55
+ false로 하면 버킷의 목록만 list형태로 출력.
56
+
57
+ s3_resource.get_object_info(bucket, file_path)
58
+ - parameter로 입력된 object의 마지막 수정일시(YYYY-MM-DD HH:MM:SS 꼴)와 s3link를 출력해준다.
59
+
60
+ s3_resource.get_object_list(bucket, s3_prefix, after_ts=0, pattern=None, tqdm_=True)
61
+ - s3_prefix는 버킷 이후 공통되는 경로 입력
62
+ - after_ts는 입력된 이후 추가/수정된 데이터만 추리기 위함(포맷 : "2023-02-28 00:00:00")
63
+ - patter은 확장자와 같은 부분
64
+ - object들의 path를 list로 반환한다.
65
+
66
+ s3_resource.download_object(bucket_name, target_file_path, download_file_path)
67
+ - S3에 있는 object를 local에 다운로드하기 위한 함수
68
+ - target_file_path는 다운로드 하고자하는 버킷 내의 object 경로
69
+ - download_file_path는 로컬에 다운받고자 하는 경로 입력(파일의 이름 포함)
70
+
71
+ s3_resource.upload_object(bucket_name, target_file_path, upload_file_path, ExtraArgs=None)
72
+ - locla에 있는 데이터를 S3에 업로드하기 위한 함수
73
+ - target_file_path는 업로드 하고자하는 로컬의 파일 경로
74
+ - upload_file_path는 s3에 업로드 하고자하는 경로
75
+ - ExtraArgs는 metadata 예를들어 {'ContentType': "video/mp4", 'ACL': "public-read"}위와 같이 입력
76
+
77
+ s3_resource.put_object(object_body, object_name, trg_bucket)
78
+ - 메모리상으로 저장되어있는 json혹은 txt 데이터를 S3 object로 업로드하는데 사용하는 함수
79
+ - object_body는 메모리 상으로 저장되어 있는 데이터
80
+ - object_name은 업로드 하고자하는 경로및 파일명의 path
81
+ - trg_bucket은 업로드 하고자하는 버킷명
82
+ - 이미지도 업로드할 수 있으나 이때에는 아래와 같이 이미지를 PIL, io라이브러리를 이용하여 가공한 뒤 업로드를 해야합니다.
83
+ img0 = Image.fromarray(im0, mode='RGB')
84
+ out_img = io.BytesIO()
85
+ img0.save(out_img, format='png')
86
+ out_img.seek(0)
87
+
88
+ s3_resource.s3tos3_put_object(src_file_name, trg_file_name, trg_s3_config, fin_print=True)
89
+ - 서로 다른 S3계정간의 데이터를 local에 따로 다운로드 한 뒤 업로드 하는 것이 아닌 메모리상으로 불러 업로드하는 함수
90
+ - src_file_name은 옮기고자하는 s3 파일의 전체 경로(버킷명은 안적어주어도 무방 -> class를 호출할때의 config파일에서 가지고 옴)
91
+ - trg_file_name은 옮겨가야할 s3에서의 경로 입력(파일명까지)
92
+ - trg_s3_config는 옮겨가야할 s3의 config파일(확장자는 yaml)
93
+
94
+ s3_resource.move_object(src_file_name, trg_file_name, bucket)
95
+ - 같은 s3내 같은 버킷에서의 파일 이동
96
+ - src_file_name은 원본 파일
97
+ - trg_file_name은 이동되어야 할 경로
98
+
99
+ s3_resource.read_file(bucket, file_name)
100
+ - S3에 있는 파일을 메모리로 읽기 위한 함수
101
+
102
+ s3_resource.read_image(bucket, file_name, library="cv2")
103
+ - S3에 있는 이미지 파일을 메모리로 읽는 함수
104
+ - 이후 사용되는 이미지 형식에 따라 옵션에 "cv2", "PIL"을 선택하여 사용 가능
105
+
106
+ ```
107
+
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ echoss_storage/__init__.py
4
+ echoss_storage/s3_handler.py
5
+ echoss_storage.egg-info/PKG-INFO
6
+ echoss_storage.egg-info/SOURCES.txt
7
+ echoss_storage.egg-info/dependency_links.txt
8
+ echoss_storage.egg-info/requires.txt
9
+ echoss_storage.egg-info/top_level.txt
10
+ tests/test_s3_handler.py
@@ -0,0 +1,6 @@
1
+ boto3~=1.19.0
2
+ opencv-python==4.8.0.74
3
+ numpy>=1.22.3
4
+ tqdm==4.63.2
5
+ pillow==10.1.0
6
+ echoss-fileformat>=1.1.0
@@ -0,0 +1 @@
1
+ echoss_storage
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,23 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r") as fh:
4
+ long_description = fh.read()
5
+
6
+ package_name = "echoss_storage"
7
+
8
+ setup(
9
+ name='echoss-storage',
10
+ version='1.1.4',
11
+ url='',
12
+ install_requires=['boto3~=1.19.0', 'opencv-python==4.8.0.74', 'numpy>=1.22.3', 'tqdm==4.63.2', 'pillow==10.1.0', 'echoss-fileformat>=1.1.0'],
13
+ license='',
14
+ author='ckkim',
15
+ author_email='ckkim@12cm.co.kr',
16
+ description='echoss AI Bigdata Solution - Object Storage like S3 handler',
17
+ long_description=long_description,
18
+ long_description_content_type='text/markdown',
19
+ packages=find_packages(),
20
+ keywords=['echoss', 'object storage', 's3handler', 's3_handler'],
21
+ package_data={},
22
+ python_requires= '>3.7',
23
+ )
@@ -0,0 +1,30 @@
1
+ import json
2
+ import os
3
+ import sys
4
+
5
+ from echoss_fileformat import FileUtil
6
+
7
+ sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname('../'))))
8
+ from echoss_storage import S3ResourceHandler
9
+
10
+
11
+ env = 'develop'
12
+ env_config = FileUtil.dict_load('env_config.yaml')[env]
13
+ s3_config = env_config['s3']
14
+
15
+ test_s3 = S3ResourceHandler(s3_config)
16
+
17
+ bucket_list = test_s3.get_bucket_list(include_date=True)
18
+
19
+ object_json_list = test_s3.get_object_list(s3_config['bucket'], "", pattern=".json")
20
+
21
+ for obj in object_json_list:
22
+ obj_fileview = test_s3.get_object_file(s3_config['bucket'], obj)
23
+ json_data = obj_fileview.as_json()
24
+ print(f"{json_data=}")
25
+
26
+ pass
27
+
28
+
29
+
30
+