am_dot_com

CN 2023-04-28

Apr 28th, 2023
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.77 KB | None | 0 0
  1. # (C) Artur Marques, 2023
  2. import boto3
  3. import uuid # UUID objects (universally unique identifiers) according to RFC 4122
  4. from werkzeug.utils import secure_filename
  5. from flask import request
  6. import os.path
  7.  
  8. # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html
  9. # s3.upload_file( ... )
  10. # for public access, provide an adequate ACL
  11. # ExtraArgs={'ACL': 'public-read'}
  12.  
  13. #_.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  14. class AmAwsS3Helper:
  15. #probabily in the [default] section of the credentials file
  16. AWS_ACCESS_KEY_ID:str="aws_access_key_id"
  17. AWS_SECRET_ACCESS_KEY:str="aws_secret_access_key"
  18. #probably in the [default] section of the config file
  19. REGION:str="region"
  20.  
  21. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  22. """
  23. 2022-04-04 - created
  24. receives the path of an AWS config text file
  25. extracts all the pairs key = value that it finds
  26. """
  27. @staticmethod
  28. def extractAllKVsFromAwsConfigFile(
  29. pStrAwsConfigFilePath: str,
  30. pbDebug: bool = True
  31. ):
  32. dictKVs = dict()
  33.  
  34. bConfigFilePathReceived = os.path.isfile(pStrAwsConfigFilePath)
  35. if (bConfigFilePathReceived):
  36. try:
  37. fr = open(
  38. file=pStrAwsConfigFilePath,
  39. mode="r"
  40. )
  41. listLinesSensitiveContent = fr.readlines()
  42. fr.close()
  43.  
  44. for strLine in listLinesSensitiveContent:
  45. strStrippedLine = strLine.strip()
  46. bLineStartsWithRectangularOpeningBracket = strStrippedLine.find("[")==0
  47. if(not bLineStartsWithRectangularOpeningBracket): #not the opening of a section
  48. iEqualPos=strStrippedLine.find("=")
  49. if(iEqualPos!=-1):
  50. strKey = strStrippedLine[0:iEqualPos-1].strip()
  51. strValue = strStrippedLine[iEqualPos+1:].strip()
  52. dictKVs[strKey] = strValue
  53. #if there was an equal "=" in the line, as in k=v
  54. #if line was not a section opening
  55. #for every line
  56. except Exception as e:
  57. if(pbDebug):
  58. print(str(e))
  59. #else
  60. #except
  61. #if config file path received
  62.  
  63. return dictKVs
  64. # def extractAllKVsFromAwsConfigFile
  65.  
  66. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  67. ACCEPTABLE_ERRORS = [
  68. "BucketAlreadyOwnedByYou"
  69. ]
  70.  
  71. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  72. """
  73. 2022-03-?? - created
  74. 2022-04-04 - added option to authenticate via explicit credentials and config files
  75. """
  76. def __init__(
  77. self,
  78. pStrCredentialsFilePath:str="",
  79. pStrConfigFilePath:str="",
  80. pbDebug:bool=True
  81. ):
  82. bCredentialsFilePathReceived = pStrCredentialsFilePath!="" and os.path.isfile(pStrCredentialsFilePath)
  83. bConfigFilePathReceived = pStrConfigFilePath!="" and os.path.isfile(pStrConfigFilePath)
  84. if(bCredentialsFilePathReceived and bConfigFilePathReceived): #only if BOTH!, because the region is a must for explicit bucket creation
  85. dictCredentials = AmAwsS3Helper.extractAllKVsFromAwsConfigFile(pStrCredentialsFilePath)
  86. dictConfig = AmAwsS3Helper.extractAllKVsFromAwsConfigFile(pStrConfigFilePath)
  87.  
  88. bGotKeyId = AmAwsS3Helper.AWS_ACCESS_KEY_ID in dictCredentials.keys()
  89. bGotSecret = AmAwsS3Helper.AWS_SECRET_ACCESS_KEY in dictCredentials.keys()
  90. bGotRegion = AmAwsS3Helper.REGION in dictConfig.keys()
  91.  
  92. bGotAllNeeded = bGotKeyId and bGotSecret and bGotRegion
  93.  
  94. if (bGotAllNeeded):
  95. # About creating AWS Buckets
  96. # Unless your region is in the United States,
  97. # Define the region *explicitly* when creating a bucket.
  98. # Or... IllegalLocationConstraintException.
  99. self.mCurrentSession = boto3.session.Session(
  100. aws_access_key_id=dictCredentials[AmAwsS3Helper.AWS_ACCESS_KEY_ID],
  101. aws_secret_access_key=dictCredentials[AmAwsS3Helper.AWS_SECRET_ACCESS_KEY],
  102. region_name=dictConfig[AmAwsS3Helper.REGION]
  103. )
  104. else:
  105. # provides a session object, authenticated via alternate mechanisms (%userprofile%\credentials or aws configure)
  106. self.mCurrentSession = boto3.session.Session()
  107. else:
  108. # provides a session object, authenticated via alternate mechanisms (%userprofile%\credentials or aws configure)
  109. self.mCurrentSession = boto3.session.Session()
  110.  
  111. self.mbDebug:bool=pbDebug
  112.  
  113. self.mS3C = boto3.client('s3') #<class 'botocore.client.S3'>
  114. if(self.mbDebug):
  115. print(self.mS3C)
  116. # <botocore.client.S3 object at 0x00000024B771BB50>
  117. #if
  118.  
  119. self.mS3R = boto3.resource('s3')#<class 'boto3.resources.factory.s3.ServiceResource'>
  120. if(self.mbDebug):
  121. print(self.mS3R)
  122. # s3.ServiceResource()
  123. #if
  124.  
  125. self.mDictBucketConfig = dict()
  126.  
  127. bCanCreateBucketConfiguration:bool = self.mCurrentSession and self.mCurrentSession.region_name!=None
  128.  
  129. if (bCanCreateBucketConfiguration):
  130.  
  131. """
  132. self.mDictBucketConfig = {
  133. # "LocationConstraint": "eu-west-1" #'IllegalLocationConstraintException'
  134. "LocationConstraint": "eu-west-2" # OK if it matches the config set
  135. }
  136. """
  137. self.mDictBucketConfig = {
  138. "LocationConstraint":self.mCurrentSession.region_name
  139. }
  140. #if could create a config for buckets
  141. #def __init__
  142.  
  143. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  144. @staticmethod
  145. def genBucketName(
  146. pStrRelaxedBucketName:str,
  147. pbAssureUnique:bool=True
  148. ):
  149. if(pbAssureUnique):
  150. oUUID = uuid.uuid4() # Generate a random UUID #<class 'uuid.UUID'>
  151.  
  152. strUUID = str(oUUID) # UUID object as string #e.g. '6c91e21b-18c4-449b-95e5-c80ec49c9dee'
  153.  
  154. strRet = pStrRelaxedBucketName + strUUID # e.g. 'myBucket6c91e21b-18c4-449b-95e5-c80ec49c9dee'
  155.  
  156. listOfStringsToJoin = [pStrRelaxedBucketName, "-", strUUID]
  157. strRet2 = "".join(listOfStringsToJoin) # e.g. 'myBucket6c91e21b-18c4-449b-95e5-c80ec49c9dee'
  158.  
  159. """
  160. Example of created name in strRet:
  161. my-bucketf680820b-d34b-4368-8042-6afb887ca246
  162.  
  163. Example of created name in strRet2:
  164. my-bucket-f680820b-d34b-4368-8042-6afb887ca246
  165. """
  166.  
  167. # TODO: observe all naming rules, e.g. AWS bucket names must be [3, 63] symbols long
  168. return strRet2
  169. else:
  170. # TODO: observe all naming rules, e.g. AWS bucket names must be [3, 63] symbols long
  171. return pStrRelaxedBucketName
  172. #if-else
  173. #def genBucketName
  174.  
  175. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  176. """
  177. 2022-04-07 - added an option to configure the created bucket
  178. """
  179. def createBucket(
  180. self,
  181. pStrRelaxedBucketName:str,
  182. pbAssureUnique:bool=True,
  183. pDictBucketConfig:dict=None
  184. ):
  185. # the name can be "relaxed" (no UUID) id the genBucketName method is called with pbAssureUnique=False
  186. strNameForBucket = AmAwsS3Helper.genBucketName(
  187. pStrRelaxedBucketName=pStrRelaxedBucketName,
  188. pbAssureUnique=pbAssureUnique
  189. )
  190.  
  191. if(pDictBucketConfig==None):
  192. pDictBucketConfig=self.mDictBucketConfig
  193. try:
  194. theBucket = self.mS3R.create_bucket(
  195. Bucket=strNameForBucket,
  196. #CreateBucketConfiguration=self.mDictBucketConfig
  197. CreateBucketConfiguration=pDictBucketConfig
  198. )
  199.  
  200. if (self.mbDebug):
  201. print("Bucket creation result " + str(theBucket))
  202. # if pbDebug
  203.  
  204. return strNameForBucket, theBucket #tuple
  205. except Exception as e:
  206. print(str(e))
  207. dictError = e.response['Error']
  208. strErrorCode = dictError['Code']
  209. bAcceptableError = strErrorCode in AmAwsS3Helper.ACCEPTABLE_ERRORS
  210.  
  211. if(bAcceptableError):
  212. theBucket = self.mS3R.Bucket(
  213. name=strNameForBucket
  214. )
  215. return strNameForBucket, theBucket #tuple
  216. #if
  217. # try-except
  218.  
  219. return "", None #no name, no bucket if it fails
  220. # def createBucket
  221.  
  222. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  223. """
  224. 2022-04-02
  225. renamed from: writeFileToBucketViaResource
  226. to: uploadLocalFileToBucketViaResource
  227. """
  228. def uploadLocalFileToBucketViaResource(
  229. self,
  230. pStrBucketName:str,
  231. pStrFileName:str
  232. ):
  233. try:
  234. #<class 'boto3.resources.factory.s3.Object'>
  235. theFileObject =\
  236. self.mS3R.Object(
  237. pStrBucketName,
  238. pStrFileName
  239. )
  240.  
  241. #upload_file returns None
  242. uploadResult =\
  243. theFileObject.upload_file(
  244. Filename=pStrFileName
  245. )
  246.  
  247. return True
  248. except Exception as e:
  249. print(str(e))
  250. #try-except
  251.  
  252. return False
  253. #def uploadLocalFileToBucketViaResource
  254.  
  255. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  256. """
  257. 2022-04-02 - created
  258. """
  259. def createFakeDirectoryPathInBucket(
  260. self,
  261. pStrBucketName:str,
  262. pStrDirPathToCreate:str
  263. ):
  264. #dict_keys(['ResponseMetadata', 'ETag'])
  265. #ETag example: '"d41d8cd98f00b204e9800998ecf8427e"'
  266. dictRet =\
  267. self.mS3C.put_object(
  268. Bucket=pStrBucketName,
  269. Key=(pStrDirPathToCreate + '/') #by ending in /, "it is a dir", although that concept does not really exist for buckets
  270. )
  271. return dictRet
  272. #def createFakeDirectoryPathInBucket
  273.  
  274. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  275. """
  276. 2022-04-02
  277. renamed from: writeFileToBucketAltViaResource
  278. to: uploadLocalFileToBucketAltViaResource
  279. """
  280. def uploadLocalFileToBucketAltViaResource(
  281. self,
  282. pStrBucketName: str,
  283. pStrFileName: str
  284. ):
  285. try:
  286. theBucketObjectWithThatName = self.mS3R.Bucket(
  287. name=pStrBucketName
  288. )
  289.  
  290. #upload_file returns None
  291. uploadResult = \
  292. theBucketObjectWithThatName.upload_file(
  293. Filename=pStrFileName,
  294. Key=pStrFileName
  295. )
  296.  
  297. return True
  298. except Exception as e:
  299. if (self.mbDebug):
  300. print(str(e))
  301. #if
  302. #try-except
  303.  
  304. return False
  305. # def uploadLocalFileToBucketAltViaResource
  306.  
  307. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  308. """
  309. 2022-04-02
  310. renamed from: writeFileToBucketViaClient
  311. to: uploadLocalFileToBucketViaClient
  312.  
  313. 2022-04-03
  314. added param pStrKeyForObjectAkaDestinationPath,
  315. to control the name of the destination object, supporting custom paths
  316. 2022-04-05
  317. added a default value to the 3rd param and made it optional, for retro-compatibility with earlier usage examples
  318. """
  319. def uploadLocalFileToBucketViaClient(
  320. self,
  321. pStrBucketName: str,
  322. pStrSourceFileName: str,
  323. pStrKeyForObjectAkaDestinationPath:str=""
  324. ):
  325. if (pStrKeyForObjectAkaDestinationPath==""):
  326. pStrKeyForObjectAkaDestinationPath=pStrSourceFileName #use same name as source
  327.  
  328. try:
  329. uploadResult = \
  330. self.mS3C.upload_file(
  331. Bucket=pStrBucketName,
  332. Filename=pStrSourceFileName,
  333. Key=pStrKeyForObjectAkaDestinationPath
  334. )
  335.  
  336. return True
  337. except Exception as e:
  338. #on Windows, with temp files: [Errno 13] Permission denied: 'c:\\Temp\\4\\flaskapptev_8kd_'
  339. print(str(e))
  340. # try-except
  341.  
  342. return False
  343. # def uploadLocalFileToBucketViaClient
  344.  
  345. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  346. DEFAULT_DESTINATION_DIR = "." #was "./dls" until 2022-04-05
  347. def downloadFileFromBucket(
  348. self,
  349. pStrBucketName: str,
  350. pStrFileName: str,
  351. pStrDestinationDir:str=DEFAULT_DESTINATION_DIR
  352. ):
  353. try:
  354. theObject = self.mS3R.Object(
  355. pStrBucketName, #s3 bucket name
  356. pStrFileName #s3 file name
  357. )
  358. if(theObject):
  359. dlResult =\
  360. theObject.download_file(
  361. pStrDestinationDir+"/"+pStrFileName
  362. )
  363.  
  364. return True
  365. #if
  366. except Exception as e:
  367. print(str(e))
  368. # try-except
  369.  
  370. return False
  371. # def downloadFileFromBucket
  372.  
  373. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  374. """
  375. 2022-04-02 - created
  376. """
  377. def readFileInBucket(
  378. self,
  379. pStrBucketName: str,
  380. pStrFileName: str
  381. ):
  382. try:
  383. theObjectInTheBucket = self.mS3R.Object(
  384. pStrBucketName,
  385. pStrFileName
  386. )
  387. content = theObjectInTheBucket.get()['Body'].read()
  388. return content
  389. except Exception as e:
  390. if(self.mbDebug):
  391. print(str(e))
  392. #try-except
  393.  
  394. return False
  395. #def readFileInBucket
  396.  
  397. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  398. """
  399. 2022-04-03 - created
  400. """
  401. def writeContentToFileToBucket(
  402. self,
  403. pStrBucketName: str,
  404. pStrFileName: str, # a key de destino (como que a path de destino, no bucket)
  405. pContent=None # o conteúdo pode ser de qualquer MIME type
  406. ):
  407. try:
  408. theObjectInTheBucket = self.mS3R.Object(
  409. pStrBucketName,
  410. pStrFileName
  411. )
  412. dictPutResponse = theObjectInTheBucket.put(
  413. Body=pContent
  414. )
  415.  
  416. strObjectUrl:str =\
  417. self.getObjectUrlFromBucketNameObjectKeySessionRegion(
  418. pStrBucketName=pStrBucketName,
  419. pStrKeyForObjectAkaDestinationPath=pStrFileName,
  420. pRegion=self.mCurrentSession.region_name
  421.  
  422. )
  423.  
  424. return dictPutResponse, strObjectUrl
  425. except Exception as e:
  426. if (self.mbDebug):
  427. print(str(e))
  428. # try-except
  429.  
  430. return False
  431. # def writeContentToFileToBucket
  432.  
  433. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  434. """
  435. 2022-04-02 - created
  436. 2022-04-04 - changed from static to dynamic
  437. 2022-04-07 - noticed that, after all, had no code to save to local path, so renamed 3rd param to pStrCloudBucketDestinationPath
  438. """
  439. def saveFlaskUploadedFileToS3Bucket(
  440. self,
  441. pStrBucketName: str,
  442. pHtmlFileElementName: str,
  443. pStrCloudBucketDestinationPath: str = "",
  444. pbAssureUniqueBucketName: bool = False
  445. ):
  446. bCheck = request.method == "POST" and request.files[pHtmlFileElementName] != ""
  447.  
  448. if (bCheck):
  449. """
  450. if the bucket does NOT exist, it is created and returned
  451. if it exists, it returns the existing bucket
  452. if the bucket name is invalid, it fails with an exception
  453. """
  454. theDestinationBucket = self.createBucket(
  455. pStrRelaxedBucketName=pStrBucketName,
  456. pbAssureUnique=pbAssureUniqueBucketName
  457. )
  458.  
  459. if (theDestinationBucket != False):
  460. # dict_keys(['ResponseMetadata', 'ETag'])
  461. dictObjectCreationResult = self.createFakeDirectoryPathInBucket(
  462. pStrBucketName=pStrBucketName,
  463. pStrDirPathToCreate=pStrCloudBucketDestinationPath
  464. )
  465. # <class 'werkzeug.datastructures.FileStorage'> #content_length, content_type, filename, headers, mimetye, mimetype_params, name (html name), stream
  466. fileStorageObjectRepresentingTheUploadedFile = request.files[pHtmlFileElementName] # e.g. "00.txt"
  467. strSecureFilename = secure_filename(
  468. fileStorageObjectRepresentingTheUploadedFile.filename) # e.g. "00.txt"
  469.  
  470. if (strSecureFilename != ""):
  471. saveResultIsAlwaysNone = \
  472. fileStorageObjectRepresentingTheUploadedFile.save(
  473. dst=strSecureFilename
  474. )
  475. # strSecureFilename was saved to the system running Flask, so it sets the pStrSourceFileName
  476. # TODO: check if file was indeed saved
  477. # TODO: save to temporary file and upload the temporary file
  478.  
  479. strDestinationPath = pStrCloudBucketDestinationPath + "/" + strSecureFilename
  480.  
  481. bUploadResult: bool = \
  482. self.uploadLocalFileToBucketViaClient(
  483. pStrBucketName=pStrBucketName,
  484. pStrSourceFileName=strSecureFilename,
  485. # RTE: the system cannot find the file is the (local) path is not available
  486. pStrKeyForObjectAkaDestinationPath=strDestinationPath
  487. )
  488.  
  489. return bUploadResult, strSecureFilename
  490. else:
  491. return False # no bucket
  492. else:
  493. return False # not post or no uploaded file
  494. # def saveFlaskUploadedFileToS3Bucket
  495.  
  496. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  497. """
  498. 2022-04-07 - created
  499. 2022-04-07 - noticed that, after all, had no code to save to local path, so renamed 3rd param to pStrCloudBucketDestinationPath
  500. """
  501. def saveFlaskUploadedPluralFilesToS3Bucket(
  502. self,
  503. pStrBucketName: str,
  504. pHtmlFileElementName: str,
  505. pStrCloudBucketDestinationPath: str = "",
  506. pbAssureUniqueBucketName: bool = False
  507. ):
  508. dictUploadResults = dict()
  509.  
  510. bCheck = request.method == "POST" and request.files[pHtmlFileElementName] != ""
  511.  
  512. if (bCheck):
  513. """
  514. if the bucket does NOT exist, it is created and returned
  515. if it exists, it returns the existing bucket
  516. if the bucket name is invalid, it fails with an exception
  517. """
  518. theDestinationBucket = self.createBucket(
  519. pStrRelaxedBucketName=pStrBucketName,
  520. pbAssureUnique=pbAssureUniqueBucketName
  521. )
  522.  
  523. if (theDestinationBucket != False):
  524. # dict_keys(['ResponseMetadata', 'ETag'])
  525. dictObjectCreationResult = self.createFakeDirectoryPathInBucket(
  526. pStrBucketName=pStrBucketName,
  527. pStrDirPathToCreate=pStrCloudBucketDestinationPath
  528. )
  529. # <class 'werkzeug.datastructures.FileStorage'> #content_length, content_type, filename, headers, mimetye, mimetype_params, name (html name), stream
  530. listFileStorageObjectsRepresentingTheUploadedFiles:list = request.files.getlist(pHtmlFileElementName)
  531. for oFileStorageObjectRepresentingSingleUploadedFile in listFileStorageObjectsRepresentingTheUploadedFiles:
  532. strSecureFilename:str = secure_filename(
  533. oFileStorageObjectRepresentingSingleUploadedFile.filename) # e.g. "00.txt"
  534.  
  535. if (strSecureFilename != ""):
  536. saveResultIsAlwaysNone = \
  537. oFileStorageObjectRepresentingSingleUploadedFile.save(
  538. dst=strSecureFilename
  539. )
  540. # strSecureFilename was saved to the system running Flask, so it sets the pStrSourceFileName
  541. # TODO: check if file was indeed saved
  542. # TODO: save to temporary file and upload the temporary file
  543.  
  544. if(pStrCloudBucketDestinationPath!=""):
  545. strDestinationPath = pStrCloudBucketDestinationPath + "/" + strSecureFilename
  546. else:
  547. strDestinationPath = strSecureFilename
  548. #if-else
  549.  
  550. bUploadResult: bool = \
  551. self.uploadLocalFileToBucketViaClient(
  552. pStrBucketName=pStrBucketName,
  553. pStrSourceFileName=strSecureFilename,
  554. # RTE: the system cannot find the file is the (local) path is not available
  555. pStrKeyForObjectAkaDestinationPath=strDestinationPath
  556. )
  557.  
  558. dictUploadResults[strSecureFilename] = bUploadResult
  559. #if could get strSecureFilename
  560. #for every uploaded object
  561. return dictUploadResults
  562. else:
  563. return False # no bucket
  564. else:
  565. return False # not post or no uploaded file
  566. # def saveFlaskUploadedPluralFilesToS3Bucket
  567.  
  568. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  569. """
  570. 2022-04-07 - created
  571. Had problems with CORRECT relative (to flask app root) paths.
  572. The only solution that never failed was writing to absolute paths, so upon call, make sure to join application.root with desired folder
  573. """
  574. @staticmethod
  575. def saveFlaskUploadedPluralFilesToFileSystemPath(
  576. pHtmlFileElementName: str,
  577. pStrAbsoluteDestinationPath: str = ""
  578. ):
  579. dictUploadResults = dict()
  580.  
  581. bCheck = request.method == "POST" and request.files[pHtmlFileElementName] != ""
  582.  
  583. if (bCheck):
  584. # <class 'werkzeug.datastructures.FileStorage'> #content_length, content_type, filename, headers, mimetye, mimetype_params, name (html name), stream
  585. listFileStorageObjectsRepresentingTheUploadedFiles:list = request.files.getlist(pHtmlFileElementName)
  586. for oFileStorageObjectRepresentingSingleUploadedFile in listFileStorageObjectsRepresentingTheUploadedFiles:
  587. strSecureFilename:str = secure_filename(
  588. oFileStorageObjectRepresentingSingleUploadedFile.filename
  589. )
  590.  
  591. if (strSecureFilename != ""):
  592. if (pStrAbsoluteDestinationPath != ""):
  593. strDestinationPath = pStrAbsoluteDestinationPath + "/" + strSecureFilename
  594. else:
  595. strDestinationPath = strSecureFilename
  596. #if-else
  597.  
  598. saveResultIsAlwaysNone = \
  599. oFileStorageObjectRepresentingSingleUploadedFile.save(
  600. dst=strDestinationPath
  601. #dst=strSecureFilename
  602. )
  603.  
  604. try:
  605. fr = open(strDestinationPath, 'rb')
  606. dictUploadResults[strSecureFilename]=fr.__sizeof__()
  607. #os.path.getsize(strDestinationPath)
  608. fr.close()
  609. except Exception as e:
  610. print(str(e))
  611. dictUploadResults[strSecureFilename] = False
  612. #try-except
  613. #if could get strSecureFilename
  614. #for every uploaded object
  615. return dictUploadResults
  616. else:
  617. return False # not post or no uploaded file
  618. # def saveFlaskUploadedPluralFilesToFileSystemPath
  619.  
  620. # _.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-._.-^-.
  621. """
  622. 2022-04-25 - created
  623. """
  624. def getObjectUrlFromBucketNameObjectKeySessionRegion(
  625. self,
  626. pStrBucketName:str,
  627. pStrKeyForObjectAkaDestinationPath:str,
  628. pRegion:str=None
  629. ):
  630. if(pRegion==None or pRegion.strip()==""):
  631. pRegion = self.mCurrentSession.region_name
  632. # if
  633.  
  634. # if the upload succeeds, the object will become addressable at an URL such as:
  635. # https://am-first-bucketa88eb7c7-0b7d-4189-ba5d-d2f120b340d8.s3.eu-west-2.amazonaws.com/20230420/file.txt
  636. strObjectUrl = f"http://{pStrBucketName}.s3.{pRegion}.amazonaws.com/{pStrKeyForObjectAkaDestinationPath}"
  637. return strObjectUrl # 2023-04-25
  638. # def getObjectUrlFromBucketNameObjectKeySessionRegion
  639. #class AmAwsS3Helper
  640.  
  641. ***************+
  642.  
  643.  
  644. # application.py
  645. import os
  646.  
  647. from amutil_cloud_aws_only_v230420 import AmAwsS3Helper
  648. MY_PREV_CREATED_BUCKET = "b1-62975a53-e434-4446-997c-98511303b870"
  649.  
  650. from flask import Flask,\
  651. request, \
  652. render_template
  653.  
  654. from werkzeug.utils import \
  655. secure_filename
  656.  
  657.  
  658. UPLOAD_ELEMENT = "nameFiles"
  659.  
  660. application = Flask(__name__)
  661. application.config['UPLOAD_FOLDER']="dls"
  662.  
  663. @application.route("/", methods=['GET', 'POST'])
  664. def root():
  665. return render_template("upload_interface.html")
  666. # def root
  667.  
  668. @application.route("/upload", methods=['POST'])
  669. def upload():
  670. theFile = request.files[UPLOAD_ELEMENT]
  671. if(theFile):
  672. src_filename = theFile.filename
  673. sfilename = secure_filename(src_filename)
  674. if(sfilename!=""):
  675. # foi possível obter um "secure" filename
  676. # dst_filename = f"{application.config['UPLOAD_FOLDER']}/{sfilename}"
  677.  
  678. # dst_filename = f"{application.config['UPLOAD_FOLDER']}{os.sep}{sfilename}"
  679.  
  680. dst_filename:str = os.path.join(
  681. application.config['UPLOAD_FOLDER'],
  682. sfilename
  683. )
  684.  
  685. # melhorável
  686. # alt: leitura por chunks
  687. theContent = theFile.stream.read()
  688.  
  689. mimeType = theFile.mimetype # not explored
  690.  
  691. try:
  692. # TODO nova versão que não faz save do ficheiro
  693. s3 = AmAwsS3Helper()
  694.  
  695. dictPutResponse, strObjectUrl =\
  696. s3.writeContentToFileToBucket(
  697. pStrBucketName=MY_PREV_CREATED_BUCKET,
  698. pStrFileName=dst_filename, # é a object key
  699. pContent=theContent
  700. )
  701.  
  702. # http://b1-62975a53-e434-4446-997c-98511303b870.s3.eu-west-2.amazonaws.com/dls/20230428_esgts_cn.txt
  703. """
  704. podemos consumir diretamento o endereço?
  705. não, pq:
  706. - o objeto só está acessível ao criado (iam_boto3);
  707. - se o objeto tiver sido tornado readable by "everyone"
  708. - teríamos que nos ter preocupado com 2 meta data:
  709. -- content-disposition = inline (para NÃO ser downloaded)
  710. -- content-type = text/plain
  711. """
  712. strAnchorForUrl = f"<a href='{strObjectUrl}'>{strObjectUrl}</a>"
  713. return strAnchorForUrl
  714. except Exception as e:
  715. # fail
  716. fb: str = \
  717. f"{str(e)} : FAILED to save file to {dst_filename}"
  718. return fb
  719. else:
  720. return f"Could not secure file name for {src_filename}"
  721. # if-else
  722. else:
  723. return f"No file was received."
  724. # if-else
  725. # def upload
  726.  
  727. if(__name__=='__main__'):
  728. application.run(
  729. #host
  730. #port
  731. debug=True
  732. )
Advertisement
Add Comment
Please, Sign In to add comment