SimpleCloud Part 4 – Storage

Now that we’ve gotten some job processing code done, let’s get into the good stuff.  The first thing we’re going to look at is the storage mechanism in SimpleCloud.  The example we used was uploading an image to the server so it could be resized for viewing at multiple resolutions or junk.  Now, you could simply attach the file contents to the job class, serialize it and unserialize it on the other side.  But the Job Queue server is really not designed for that (nor are most other queueing applications).  So what we’re going to do is use the Storage mechanism in SimpleCloud (in this case, S3) to store the files temporarily and then for the resized versions.

The first thing we need to do is create the adapter.  I am simply putting it into a Zend_Registry object for later retrieval.  It, along with the Document and Queue adapters, are created in the bootstrap file.  The bootstrap file loads the autoloader, creates the config objects and then creates all of the cloud adapters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
set_include_path(
    realpath(__DIR__ . '/../library')
    . PATH_SEPARATOR
    . get_include_path()
);
 
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
 
use com\zend\jobqueue\Manager;
 
$config = new Zend_Config_Ini(__DIR__.'/../config/jobqueue.ini');
Zend_Registry::set(Manager::CONFIG_NAME, $config);
$config = new Zend_Config_Ini(__DIR__.'/../config/config.ini');
 
Zend_Registry::set('Config', $config);
 
Zend_Registry::set(
    'StorageAdapter',
    Zend_Cloud_StorageService_Factory::getAdapter(
        $config->cloud
    )
);

 

The most important line is the getAdapter() line.  That takes the configuration options and creates an adapter based on those options.  It’s really quite simple.  In this case I’m using the S3 adapter.

1
2
3
4
cloud.storage_adapter="Zend_Cloud_StorageService_Adapter_S3"
cloud.aws_accesskey="XXXXXXXXXXXXXXXXXXX"
cloud.aws_secretkey="XXXXXXXXXXXXXXXXXXX"
cloud.bucket_name="zendcapimages"

A bucket name needs to be specified, and I believe it needs to be created ahead of time.  This allows you to separate your applications but still use the same account keys.  Easy, huh?  You haven’t even tried using it yet!  Here is the job (distilled to the essentials; full version will be downloadable) that is used to process the images.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
namespace org\eschrade\jobs;
 
use com\zend\jobqueue\JobAbstract;
 
class ProcessImages extends JobAbstract
{
 
    private $_resolutions = array();
    private $_sourceFile;
    private $_sourceId;
 
    public function __construct()
    {
        $this->_sourceId = sha1(uniqid(php_uname(), true));
    }
 
    public function setSourceFile($file)
    {
 
        $fileName = 'tmp/fileProcess-' . sha1(uniqid(php_uname(), true));
        $storage = Zend_Registry::get('StorageAdapter');
        $storage->storeItem($fileName, $file);
        $this->_sourceFile = $fileName;
    }
 
    protected function _execute()
    {
        try {
 
            $storageAdapter = Zend_Registry::get('StorageAdapter');
 
            foreach ($this->_resolutions as $width) {
 
                $image = imagecreatefromstring($storageAdapter->fetchItem($this->_sourceFile));
 
// cut
                if ($res) {
                    $fileName = 'public/' . $this->_sourceId . '-' . $width . '.png';
                    $tmpfname = tempnam("/tmp", $this->_sourceId);
                    if ($tmpfname && imagepng($newImage, $tmpfname)) {
                        $storageAdapter->storeItem($fileName, fopen($tmpfname, 'r'));
// cut
                       continue;
                    }    
                }
// cut
           }
        } catch (Exception $e) {
            zend_monitor_set_aggregation_hint(time());
            zend_monitor_custom_event("Internal Server Error", $e->getMessage(), array($e->getTraceAsString()));
        }
    }
}

 

The parts pertaining to the document adapter have been bolded.  The point here is that the storage and retrieval of file data is pretty much transparent.  Store/Fetch.  Integrating between the front and back end is pretty easy, too.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $req = new Zend_Controller_Request_Http();
    $response = new Zend_Controller_Response_Http();
    if ($upload->isValid($req->getPost())) {
 
        $procTask = new ProcessImages();
        $upload->file->receive();
        $fileName = $upload->file->getFileName();
        $procTask->setSourceFile( file_get_contents($fileName) );
 
        $session = new Zend_Session_Namespace('ProcTask');
        $session->response    = $procTask->execute();
 
        $response->setRedirect('status.php')->sendHeaders();        
        exit;
    }
}

 

So, what is going on here?  I’ve bolded the most important parts.  When we call setSourceFile() this calls the code that uploads the file to S3.  Additionally, IIRC, there is also a stream API where you can pass a file resource and it uses that instead of the simple file contents.  That’s very useful for storing large files.  But remember in the earlier post where I said that calling execute() doesn’t actually execute it, but queues it, and that the result is a response object that provides the job number and the server host name?  There you see it getting attached the the session.  This code then forwards to another page, which we will look at in a bit.

But, as you can see, using SimpleCloud to upload files to a storage service is stupid easy when using Zend Framework.

Related posts

Leave a Comment