Advertisement
ericsaupe

AWS S3 Python Boto File Utils Example

Dec 3rd, 2013
408
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.34 KB | None | 0 0
  1. class FileUtils:
  2.     def __init__(self, companyID):
  3.         self.companyID = companyID
  4.         self.rootFolder = '%sClient Number/%s/' % (settings.FILE_ROOT, companyID)
  5.         self.s3Connection = boto.connect_s3() #This opens the connection to our s3 instance
  6.         self.s3Bucket = self.s3Connection.get_bucket(settings.S3_BUCKET) #This gets the s3 bucket
  7.  
  8.     def save_file(self, path=None, data=None):
  9.         """
  10.         This method saves a file to the given path.  If none is given for either the path or the data it will return false.
  11.         Parameters:
  12.             path - The path to save the file in
  13.             data - The file to be saved
  14.  
  15.         Returns:
  16.             True if the file was saved successfully otherwise an error will be thrown.
  17.         """
  18.         if data is None or path is None:
  19.             return False
  20.         if not "Files/Client Number/" in path:
  21.             path = "%s%s" % (self.rootFolder, path)
  22.         k = self.s3Bucket.new_key(path)
  23.  
  24.         if (path[path.rfind('.'):] == '.pdf'):
  25.             k.set_contents_from_string(data, headers={'Content-Type': 'application/pdf'})
  26.         else:
  27.             k.set_contents_from_string(data)
  28.  
  29.         return self.get_contents(None, path[:path.rfind('/') + 1])
  30.  
  31.     def create_folder(self, request, path=""):
  32.         """
  33.         This method creates a folder in the given path.  If one is not given it is created in the root.
  34.  
  35.         Parameters:
  36.             path - where the folder will be created.  Empty string will create the folder in root.
  37.  
  38.         Returns:
  39.             True if the folder was created succesfully.
  40.         """
  41.         if request.POST.get('name') is None:
  42.             return False
  43.         newFolderName = request.POST.get('name', '')
  44.         if path == '':
  45.             path = self.rootFolder
  46.         newFolder = '%s%s/' % (path, newFolderName)
  47.         try:
  48.             k = self.s3Bucket.new_key(newFolder)
  49.             k.set_contents_from_string("")
  50.         except:
  51.             return HttpResponse("Folder Name in Use", mimetype="text/plain")
  52.  
  53.         return self.get_contents(None, path)
  54.  
  55.     def delete(self, request, path=None):
  56.         """
  57.         This method deletes the element given in path.
  58.  
  59.         Parameters:
  60.             path - path to the element to be deleted
  61.  
  62.         Returns:
  63.             Contents of the parent folder
  64.         """
  65.         if path is None:
  66.             return False
  67.  
  68.         #get current shares that are contained in this share and unshare them
  69.         PendingFileSharesPermissions.objects.filter(path__path__contains=path).delete()
  70.         PendingFileShares.objects.filter(path__contains=path).delete()
  71.         #get current shares that are contained in this share and unshare them
  72.         FileSharesPermissions.objects.filter(path__path__contains=path).delete()
  73.         FileShares.objects.filter(path__contains=path).delete()
  74.  
  75.         if FileUtils.is_file(path):
  76.             parent = path[:path.rfind('/') + 1]
  77.         elif FileUtils.is_folder(path):
  78.             keyList = self.s3Bucket.list(prefix=path)
  79.             for key in keyList:
  80.                 self.s3Bucket.delete_key(key.name)
  81.             parent = path[:path.rfind('/')]
  82.             parent = parent[:parent.rfind('/') + 1]
  83.         k = self.s3Bucket.get_key(path)
  84.         if (k is not None):
  85.             self.s3Bucket.delete_key(k)
  86.         return self.get_contents(None, parent)
  87.  
  88.     def get_file(self, path=None, isFile=True):
  89.         """
  90.         This method fetches the file at path and returns it.
  91.  
  92.         Parameters:
  93.             path - The path of the requested file
  94.  
  95.         Returns:
  96.             The file at the requested path
  97.         """
  98.         if path is None:
  99.             return None
  100.         if isFile:
  101.             allowedAccess = False
  102.             if path[:self.rootFolder.__len__()] == self.rootFolder:
  103.                 allowedAccess = True
  104.             else:
  105.                 shares = FileSharesPermissions.objects.filter(company=self.companyID)
  106.                 for share in shares:
  107.                     if share.path.path in path:
  108.                         allowedAccess = True
  109.                         break
  110.             if not allowedAccess:
  111.                 return False
  112.         k = self.s3Bucket.get_key(path)
  113.         url = ''
  114.         if k:
  115.             url = k.generate_url(120)
  116.         return url
  117.  
  118.     def get_raw_file(self, path=None, isFile=True):
  119.         """
  120.         This method fetches the file at path and returns it.
  121.  
  122.         Parameters:
  123.             path - The path of the requested file
  124.  
  125.         Returns:
  126.             The file at the requested path
  127.         """
  128.         if path is None:
  129.             return None
  130.         if isFile:
  131.             allowedAccess = False
  132.             if path[:self.rootFolder.__len__()] == self.rootFolder:
  133.                 allowedAccess = True
  134.             else:
  135.                 shares = FileSharesPermissions.objects.filter(company=self.companyID)
  136.                 for share in shares:
  137.                     if share.path.path in path:
  138.                         allowedAccess = True
  139.                         break
  140.             if not allowedAccess:
  141.                 return False
  142.         k = self.s3Bucket.get_key(path)
  143.         fileName = k.get_contents_as_string()
  144.         return fileName
  145.  
  146.     def move(self, request, path=None):
  147.         """
  148.         This method renames the file by copying the key to a new key with the new name and deleting the old one.
  149.         Functions like the Unix move command.
  150.  
  151.         Parameters:
  152.             request - contains POST data with the new name
  153.             path - The relative path of the item to be moved
  154.  
  155.         Returns:
  156.             Contents of the parent folder to be used in refreshing the list
  157.         """
  158.         if path is None:
  159.             return None
  160.         #Get the path
  161.         shared = True
  162.         if path[:self.rootFolder.__len__()] == self.rootFolder:
  163.             shared = False
  164.         #Move the file or folder
  165.         if FileUtils.is_file(path):
  166.             #Move places the file into the new folder
  167.             if request.POST.get('move') == 'true':
  168.                 if request.POST.get('name') != "fileslist":
  169.                     newFolder = "%s%s" % (self.rootFolder, request.POST.get('name'))
  170.                 else:
  171.                     newFolder = "%s" % (self.rootFolder)
  172.                 if not self.s3Bucket.get_key(newFolder):
  173.                     k = self.s3Bucket.new_key(newFolder)
  174.                     k.set_contents_from_string("")
  175.                 newName = "%s%s" % (newFolder, path[path.rfind('/')+1:])
  176.             #Non-Move calls rename the file in its current folder
  177.             else:
  178.                 if not self.s3Bucket.get_key(path[:path.rfind('/')+1]):
  179.                     k = self.s3Bucket.new_key(path[:path.rfind('/')+1])
  180.                     k.set_contents_from_string("")
  181.                 newName = "%s%s%s" % (path[:path.rfind('/')+1], request.POST.get('name'), path[path.rfind('.'):])
  182.             self.s3Bucket.copy_key(newName, settings.S3_BUCKET, path)
  183.             #If the file is not in a shared directory we can remove it and update file sharing if it exists
  184.             if not shared:
  185.                 shared = FileShares.objects.filter(path=path)
  186.                 for share in shared:
  187.                     share.path = newName
  188.                     share.save()
  189.                 self.s3Bucket.delete_key(path)
  190.         #Folder names need to be pulled out of their current path
  191.         elif FileUtils.is_folder(path):
  192.             #Move holds the folder name the same but appends it to the desitination folder
  193.             if request.POST.get('move') == 'true':
  194.                 newName = path[:path.rfind('/')]
  195.                 newName = newName[newName.rfind('/')+1:]
  196.                 newName = "%s%s%s/" % (self.rootFolder, request.POST.get('name'), newName)
  197.             #Rename changes the folder name
  198.             else:
  199.                 newName = path[:path.rfind('/')]
  200.                 newName = newName[:newName.rfind('/')+1]
  201.                 newName = "%s%s/" % (newName, request.POST.get('name'))
  202.             #Update any filesharing information
  203.             shared = FileShares.objects.filter(path=path)
  204.             for share in shared:
  205.                 share.path = newName
  206.                 share.save()
  207.             #Alter the contents of the folder
  208.             FileUtils._move_folder(self, path, newName)
  209.         #Get the parent to return it's contents
  210.         if FileUtils.is_file(path):
  211.             parent = path[:path.rfind('/') + 1]
  212.         elif FileUtils.is_folder(path):
  213.             parent = path[:path.rfind('/')]
  214.             parent = parent[:parent.rfind('/') + 1]
  215.         return self.get_contents(None, parent)
  216.  
  217.     def move_file(self, oldPath, newPath):
  218.         self.s3Bucket.copy_key(newPath, settings.S3_BUCKET, oldPath)
  219.         self.s3Bucket.delete_key(oldPath)
  220.         return True
  221.  
  222.     def _move_folder(self, path, name):
  223.         """
  224.         Recursive helper method to move folders and their containing files
  225.  
  226.         Parameters:
  227.             self
  228.             path - key name for the folder to be changed
  229.             name - new key name to replace old path
  230.         """
  231.         for key in self.s3Bucket.list(prefix=path, delimiter="/"):
  232.             if FileUtils.is_file(key.name):
  233.                 newName = "%s%s" %(name, key.name[key.name.rfind('/')+1:])
  234.                 self.s3Bucket.copy_key(newName, settings.S3_BUCKET, key.name)
  235.                 self.s3Bucket.delete_key(key.name)
  236.             elif FileUtils.is_folder(key.name):
  237.                 newFolder = key.name[:key.name.rfind('/')]
  238.                 newFolder = "%s%s/" % (name, newFolder[newFolder.rfind('/')+1:])
  239.                 if self.s3Bucket.get_key(key.name):
  240.                     self.s3Bucket.copy_key(newFolder, settings.S3_BUCKET, key.name)
  241.                     self.s3Bucket.delete_key(key.name)
  242.                 else:
  243.                     k = self.s3Bucket.new_key(newFolder)
  244.                     k.set_contents_from_string("")
  245.                 if key.name != path:
  246.                     FileUtils._move_folder(self, key.name, newFolder)
  247.  
  248.     @staticmethod  
  249.     def is_file(path):
  250.         """
  251.         This method may need to become more robust in the future, but it's purpose is to detect within amazon s3 whether a given path is a file.
  252.  
  253.         Parameters:
  254.             path - The path of the file
  255.  
  256.         Returns:
  257.             true if the path is a file, otherwise false
  258.         """
  259.         foldername, filename = os.path.split(path)
  260.         return filename != ''
  261.  
  262.     @staticmethod
  263.     def is_folder(path):
  264.         """
  265.         This method may need to become more robust in the future, but it's purpose is to detect within amazon s3 whether a given path is a folder.
  266.  
  267.         Parameters:
  268.             path - The path of the folder
  269.  
  270.         Returns:
  271.             true if the path is a folder, otherwise false
  272.         """
  273.         foldername, filename = os.path.split(path)
  274.         return filename == ''
  275.  
  276.     def get_contents(self, subFolder=None, path=None)
  277.         """
  278.         This method gets the file list for all files and what folder they are contained in for a given path.  It drives the recursive method that gets the contents for all folders and their subfolders.
  279.  
  280.         Parameters:
  281.             subFolder - The subfolder of the root directory to begin at
  282.             path - Explicit path to get contents
  283.  
  284.         Returns:
  285.             A dictionary of files and folders with their contents.
  286.         """
  287.         contents = self._get_contents(subFolder, path)
  288.         if path is None or path == self.rootFolder:
  289.             #now get the shared stuff
  290.             sharedContents = {'folders':[], 'files':[]}
  291.             shared = FileSharesPermissions.objects.filter(company__in=[self.companyID, 0])
  292.             for share in shared:
  293.                 path = share.path.path
  294.                 if path[:self.rootFolder.__len__()] == self.rootFolder:
  295.                     continue
  296.                 shareContents = {'folders':[], 'files':[]}
  297.                 for key in self.s3Bucket.list(prefix=path, delimiter="/"):
  298.                     if key.name.startswith("."):
  299.                         continue
  300.                     if FileUtils.is_folder(key.name):
  301.                         if key.name == path:
  302.                             continue
  303.                         shareContents['folders'].append({"foldername":key.name.replace(path, ""), "meta":{"size":'--', 'moddate':'--', 'fullpath':key.name}, 'contents':{'folders':[], 'files':[]}})
  304.  
  305.                     if FileUtils.is_file(key.name):
  306.                         s = key.size
  307.                         d = datetime.datetime.strptime(key.last_modified[:19], "%Y-%m-%dT%H:%M:%S")
  308.                         if key.name == path:
  309.                             folderName, keyName = os.path.split(path)
  310.                         else:
  311.                             keyName = key.name.replace(path, "")
  312.                         shareContents['files'].append({"filename":keyName, "meta":{"size":FileUtils.convert_bytes(s), "moddate":d.strftime("%m/%d/%y %H:%M:%S"), 'fullpath':key.name}})
  313.                 if FileUtils.is_folder(path):
  314.                     sharedContents['folders'].append({'foldername':("%s/" % os.path.basename(os.path.normpath(path))), 'meta':{'size':'--', 'moddate':'--', 'fullpath':path}, 'contents':shareContents})
  315.                 else:
  316.                     sharedContents['files'].extend(shareContents['files'])
  317.  
  318.             contents['folders'].append({"foldername":"SHARED/", "meta":{"size":"--", 'moddate':'--'}, 'contents':sharedContents})
  319.  
  320.         return contents
  321.  
  322.     def _get_contents(self, subFolder=None, path=None):
  323.         """
  324.         This method gets the file list for all files and what folder they are contained in for a given path.  It recursively gets the contents for all folders and their subfolders.
  325.  
  326.         Parameters:
  327.             subFolder - The subfolder of the root directory to begin at
  328.             path - Explicit path to get contents
  329.  
  330.         Returns:
  331.             A dictionary of files and folders with their contents.
  332.         """
  333.         if subFolder is None and path is None:
  334.             path = self.rootFolder
  335.         elif subFolder is not None and path is None:
  336.             path = "%s%s/" % (self.rootFolder, subFolder)
  337.         elif subFolder is not None and path is not None:
  338.             path = "%s%s/" % (path, subFolder)
  339.  
  340.         contents = {'folders':[], 'files':[]}
  341.         for key in self.s3Bucket.list(prefix=path, delimiter="/"):
  342.             if key.name.startswith("."):
  343.                 continue
  344.             if FileUtils.is_folder(key.name):
  345.                 #Specification files will be handled by the user through the Specification Files page
  346.                 if key.name == path or key.name == self.rootFolder + 'SPECIFICATION FILES/':
  347.                     continue
  348.                 contents['folders'].append({"foldername":key.name.replace(path, ""), "meta":{"size":'--', 'moddate':'--', 'fullpath':key.name}, 'contents':{'folders':[], 'files':[]}})
  349.  
  350.             if FileUtils.is_file(key.name):
  351.                 s = key.size
  352.                 d = datetime.datetime.strptime(key.last_modified[:19], "%Y-%m-%dT%H:%M:%S")
  353.                 contents['files'].append({"filename":key.name.replace(path, ""), "meta":{"size":FileUtils.convert_bytes(s), "moddate":d.strftime("%m/%d/%y %H:%M:%S"), 'fullpath':key.name}})
  354.  
  355.         return contents
  356.  
  357.                 @staticmethod  
  358.                 def convert_bytes(bytes):      
  359.                 """        
  360.                This is a static helper method to convert bytes into a readable format
  361.         Parameters: bytes - Bytes to be formated
  362.         Returns:    A string representation of the bytes in a more readable format     
  363.                """
  364.         bytes = float(bytes)
  365.         if bytes >= 1099511627776:
  366.             terabytes = bytes / 1099511627776
  367.             size = '%.2fTB' % terabytes
  368.         elif bytes >= 1073741824:
  369.             gigabytes = bytes / 1073741824
  370.             size = '%.2fGB' % gigabytes
  371.         elif bytes >= 1048576:
  372.             megabytes = bytes / 1048576
  373.             size = '%.2fMB' % megabytes
  374.         elif bytes >= 1024:
  375.             kilobytes = bytes / 1024
  376.             size = '%.2fKB' % kilobytes
  377.         else:
  378.             size = '%.2fB' % bytes
  379.         return size
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement