11from __future__ import annotations
22
3+ import urllib .parse
34from dataclasses import dataclass , field
45from io import BufferedReader , FileIO
56from pathlib import Path
89from httpx import HTTPError , Response
910
1011from ..constants import DEFAULT_FILE_OPTIONS , DEFAULT_SEARCH_OPTIONS
11- from ..types import BaseBucket , ListBucketFilesOptions , RequestMethod
12+ from ..types import (
13+ BaseBucket ,
14+ CreateSignedURLOptions ,
15+ ListBucketFilesOptions ,
16+ RequestMethod ,
17+ TransformOptions ,
18+ )
1219from ..utils import StorageException , SyncClient
1320
1421__all__ = ["SyncBucket" ]
@@ -44,7 +51,9 @@ def _request(
4451
4552 return response
4653
47- def create_signed_url (self , path : str , expires_in : int ) -> dict [str , str ]:
54+ def create_signed_url (
55+ self , path : str , expires_in : int , options : CreateSignedURLOptions = {}
56+ ) -> dict [str , str ]:
4857 """
4958 Parameters
5059 ----------
@@ -65,15 +74,18 @@ def create_signed_url(self, path: str, expires_in: int) -> dict[str, str]:
6574 ] = f"{ self ._client .base_url } { cast (str , data ['signedURL' ]).lstrip ('/' )} "
6675 return data
6776
68- def get_public_url (self , path : str ) -> str :
77+ def get_public_url (self , path : str , options : TransformOptions = {} ) -> str :
6978 """
7079 Parameters
7180 ----------
7281 path
7382 file path, including the path and file name. For example `folder/image.png`.
7483 """
84+ render_path = "render/image" if options .get ("transform" ) else "object"
85+ transformation_query = urllib .parse .urlencode (options )
86+ query_string = f"?{ transformation_query } " if transformation_query else ""
7587 _path = self ._get_final_path (path )
76- return f"{ self ._client .base_url } object /public/{ _path } "
88+ return f"{ self ._client .base_url } { render_path } /public/{ _path } { query_string } "
7789
7890 def move (self , from_path : str , to_path : str ) -> dict [str , str ]:
7991 """
@@ -97,6 +109,28 @@ def move(self, from_path: str, to_path: str) -> dict[str, str]:
97109 )
98110 return res .json ()
99111
112+ def copy (self , from_path : str , to_path : str ) -> dict [str , str ]:
113+ """
114+ Copies an existing file to a new path in the same bucket.
115+
116+ Parameters
117+ ----------
118+ from_path
119+ The original file path, including the current file name. For example `folder/image.png`.
120+ to_path
121+ The new file path, including the new file name. For example `folder/image-copy.png`.
122+ """
123+ res = self ._request (
124+ "POST" ,
125+ "/object/copy" ,
126+ json = {
127+ "bucketId" : self .id ,
128+ "sourceKey" : from_path ,
129+ "destinationKey" : to_path ,
130+ },
131+ )
132+ return res .json ()
133+
100134 def remove (self , paths : list ) -> dict [str , str ]:
101135 """
102136 Deletes files within the same bucket
@@ -139,7 +173,7 @@ def list(
139173 )
140174 return response .json ()
141175
142- def download (self , path : str ) -> bytes :
176+ def download (self , path : str , options : TransformOptions = {} ) -> bytes :
143177 """
144178 Downloads a file.
145179
@@ -148,10 +182,16 @@ def download(self, path: str) -> bytes:
148182 path
149183 The file path to be downloaded, including the path and file name. For example `folder/image.png`.
150184 """
185+ render_path = (
186+ "render/image/authenticated" if options .get ("transform" ) else "object"
187+ )
188+ transformation_query = urllib .parse .urlencode (options )
189+ query_string = f"?{ transformation_query } " if transformation_query else ""
190+
151191 _path = self ._get_final_path (path )
152192 response = self ._request (
153193 "GET" ,
154- f"/object/ { _path } " ,
194+ f"{ render_path } / { _path } { query_string } " ,
155195 )
156196 return response .content
157197
@@ -188,6 +228,7 @@ def upload(
188228 files = {"file" : (filename , open (file , "rb" ), headers .pop ("content-type" ))}
189229
190230 _path = self ._get_final_path (path )
231+
191232 return self ._request (
192233 "POST" ,
193234 f"/object/{ _path } " ,
0 commit comments