Quick images uploader for your blog. A real time saver!

Published on
Feb 13, 2011

Laziness is the ultimate locomotive of the Progress. Many times I realize that I need a much simpler and more efficient image uploading mechanism, simpler than the regular and annoying file dialog, and even easier than an FTP client. That's why I wrote the script below. You can just copy-paste it into your new python file, name it like upload_image.py. The script asked me to allow him to explain to you about what he's doing, and I gladly agreed with him, so what's following next - is his own explanation of the process.

Let's say my owner has a small food blog, such as this one. For each post, there can be from one to an infinite number of images. To reduce the image size, my owner needs to reduce the jpegs' sizes using, for example, the neatest David's Batch Processor. The owner is a lazy user who always leaves the minimized images in the same directory with the originals (don't ask, they sometimes think that 'directory' is a bad word). Now the jpegs' sizes are all around 45k-50k, but I want to reduce them even more (bandwidth is my other friend). I run exiv2 rm *.jpg, which removes all the thumbnails and other unneeded data from images, and now they're just as small as I wanted them to be. Then, I move the original files into another directory on the same level as the original directory, make a gzip archive and upload it: I connect to the server, go to the images directory and unzip the archive, with the containing directory. I also output some tags, so my owner would just copy-paste them into his html editor, or whatever this person has there.

As you can see, I have some settings, and to use me you should change things like username, port etc., there aren't much. And you can run me from command line by typing

python upload_pics.py -d path/to/the/images/directory

I'll ask you for your remote server password if you don't mind. Oh, and you'll need to install all the required libs which I have in the #import declaration - but only if you don't have them already.. Just select them from Synaptic or something. And please have exiv2 installed as well. That's quite it. And, hee-hee, you know, you can't ask me question, because I'm just a script so, use me, and drink more beer on your free time.

#!/usr/bin/env python
# By Val Kotlarov @webdesignpatterns.org

# This will upload files and extract them. 

import base64, getpass, os, socket, sys, traceback, tarfile, getopt, paramiko, shutil
import pyexiv2

# settings go here
username = 'username'		# change with your username
port = 22					# your connection's port number, 22 is the default
hostname = 'example.com'	# change with domain or static ip address
remote_images_directory = '/path/to/your/images/directory/'	# set to where the images directory is
max_file_size = 150000		# maximum size in bytes for images to be papcked and uploaded
remote_upload_file_path = remote_images_directory+'upload.tar.gz'
password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
# end settings

# get files in directory which are greater than given size in bytes
def listDirectory(directory, minFileByteSize):                                        
    "get list of file info objects for files of particular extensions" 
    fileList = [os.path.join(directory, os.path.normcase(f))
                for f in os.listdir(directory)]             
    #filter by file size
    fileList = [f
               for f in fileList
                if os.path.getsize(f) > minFileByteSize]
    return fileList


try:
    opts, args = getopt.getopt(sys.argv[1:], "d:v", ["help", "output="])
except getopt.GetoptError, err:
    # print help information and exit:
    print str(err) # will print something like "option -a not recognized"
    sys.exit(2)

input_dir = None     # the directory to be uploaded
for o, a in opts:
    if o in ("-d", "--directory"):
        input_dir = a
    else:
        assert False, "unhandled option"
        exit(0)


# setup logging
paramiko.util.log_to_file('upload_pics.log')


# move big files to a new directory on the same level
big_files = listDirectory(input_dir, max_file_size)		# ~150k
if big_files != []:
    os.mkdir(input_dir + '_big')
    for f in big_files:
      shutil.move(f, input_dir + '_big/'+os.path.basename(f))

#print html for images
files = listDirectory(input_dir, 0)	
for f in files:
   print ''




# remove exiv info
os.system("exiv2 rm " + input_dir  + "/*.jpg")

# store into tar file, having the archive's directory name
tar = tarfile.open(input_dir + "/upload.tar.gz", "w:gz")

# add this directory and its contents, without path to it!
tar.add(input_dir, arcname=os.path.basename(input_dir), recursive=True)		
tar.close()

# get host key, if we know one
hostkeytype = None
hostkey = None
try:
    host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
except IOError:
    try:
        # try ~/ssh/ too, because windows can't have a folder named ~/.ssh/
        host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
    except IOError:
        print '*** Unable to open host keys file'
        host_keys = {}

if host_keys.has_key(hostname):
    hostkeytype = host_keys[hostname].keys()[0]
    hostkey = host_keys[hostname][hostkeytype]
    print 'Using host key of type %s' % hostkeytype


# now, connect and use paramiko Transport to negotiate SSH2 across the connection
try:
    t = paramiko.Transport((hostname, port))
    t.connect(username=username, password=password, hostkey=hostkey)  
    sftp = paramiko.SFTPClient.from_transport(t)
    
    # upload
    print 'Inside directory: ' + input_dir
    sftp.put(input_dir+'/upload.tar.gz', remote_upload_file_path) 
    print 'Uploaded file'
 
    ch = t.open_channel(kind = "session")
    ch.exec_command('cd '+ remote_images_directory+' && tar xzf upload.tar.gz')

    print 'Finished.'
    sftp.close()
    t.close()

except Exception, e:
    print '*** Caught exception: %s: %s' % (e.__class__, e)
    traceback.print_exc()
    try:
        sftp.close()
        t.close()
    except:
        pass
    sys.exit(1)