Статьи

Использование CouchDb в качестве файловой системы с PHP

Одна из проблем, которую мне нужно решить в моих кластерных PHP-приложениях, это где хранить файлы. Когда я говорю файлы, я не говорю об исходном коде. Я говорю о дополнительных файлах данных, таких как загружаемые PDF-файлы, журналы и т. Д. Эти файлы должны быть на каждом узле кластера. Одним из возможных подходов к решению является использование распределенной файловой системы, rsync или, возможно, использование файлового сервера, смонтированного на каждом узле. Другим решением может быть использование CouchDb. CouchDb имеет две отличные функции для удовлетворения или требований с этой проблемой. Это позволяет нам хранить файлы как вложения, а также позволяет создавать отличную и очень простую систему с несколькими мастер-репликами.

Использование CouchDB довольно просто. Он реализует интерфейс RESTfull для выполнения любых операций. Так что единственное, что нам нужно, это клиент REST. У Zend Framework есть отличный вариант . Нам не нужна библиотека. Мы можем легко выполнять запросы REST с расширением PHP Curl . Я создал две библиотеки для работы с CouchDb, одна — это низкоуровневый HTTP-клиент (с curl), а другая — более высокого уровня (он использует HTTP-клиент) для операций CouchDB. Вы можете прочитать два поста об этих библиотеках здесь и здесь .

Теперь я хочу расширить возможности моей библиотеки. Я хочу использовать CouchDB в качестве хранилища файлов в PHP. Вместо использования файловых функций (fopen, fwrite, fread,…) я хочу использовать другие и хранить / читать файлы в CouchDB. Для делать это , я переработан эти две библиотеки в другую называется Ноябрем . Я также использовал пространства имен, поэтому буду использовать их в библиотеке. Это означает, что он доступен только с PHP 5.3.

Вот вам краткое изложение библиотеки. Это не полный график UML. Это всего лишь схема с основными функциями только в образовательных целях.

резюме

 

Лучше всего показать библиотеку с примером:

Сначала я собираюсь начать с базового использования библиотеки Nov \ CouchDb:

// Starting up the loader
require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\CouchDb;
$cdb = new CouchDb('localhost', 5984);
$cdb->db('users');
$nombre = $cdb->db('ris_users')->select('gonzalo')->asObject()->name;
$apellido = $cdb->db('ris_users')->select('gonzalo')->asObject()->surname;
echo "Hello {$nombre} {$apellido}.
";

To allow me the use of different CouchDb Databases and to put the Database configuration in one file. I use the following configuration class:

class NovConf
{
    const CDB1 = 'CDB1';
    const PG1  = 'PG1';

    public static $_dbs = array(
    	self::PG1  => array(
            'driver'   => 'pgsql',
            'dsn'      => 'pgsql:dbname=pg1;host=localhost',
            'username' => null
            'password' => null,
        ),
        self::CDB1  => array(
            'driver'   => 'couchdb',
            'host'     => 'localhost',
            'port'     => 5984,
            'protocol' => 'http',
            'username' => null,
            'password' => null,
        ),
    );
}

As you can see I use the same configuration file for my PDO drivers and CouchDb.

Now I can use:

require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\CouchDb;
$cdb = CouchDb::factory(NovConf::CDB1)->db('users');

try {
    $cdb->insert('xxx', array('name' => 'xxx'));
} catch (CouchDb\Exception\DupValOnIndex $e) {
    echo "Already created\n";
}

$data = $cdb->select('xxx')->asObject();
$cdb->update('xxx', array('name' => 'xxx1'));
$cdb->delete('xxx')->asObject();
require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\CouchDb;
$cdb = CouchDb::factory(NovConf::CDB1)->db('users');

try {
    $cdb->insert('xxx', array('name' => 'xxx'));
} catch (CouchDb\Exception\DupValOnIndex $e) {
    echo "Already created\n";
}

$data = $cdb->select('xxx')->asObject();
$cdb->update('xxx', array('name' => 'xxx1'));
$cdb->delete('xxx')->asObject();

And now finally the file storage part:

For storing the files I’ve taken one design decision. Every files will be stored into separate CouchDb document. That’s means one file, one document. There’s another possible approach. One CouchDb document can be one folder and store every files as attachments of this folder in the same document. But I prefer the idea of not to track folders. Only files. So each CouchDb document will have only one attachment.

That’s an example of one document in CouchDb

{
   "_id": "/home/gonzalo/aasa.txt",
   "_rev": "2-48b501a81c38fd84a3e0351917e64135",
   "path": "/home/gonzalo",
   "_attachments": {
       "aasa.txt": {
           "stub": true,
           "content_type": "application/octet-stream",
           "length": 12,
           "revpos": 2
       }
   }
}

There’s another usage script. Here we can see all the features together. We create files, update and delete them. Internally Nov\CouchDb\Fs uses a predefined CouchDb database called fs.

use Nov\CouchDb\Fs;
use Nov\CouchDb\Fs\Exception;
require_once ("Nov/Loader.php");
Nov\Loader::init();

echo "<pre>";
// create an instance from a factory method
$fs = Fs::factory(NovConf::CDB1);
// Now we're going to delete a file. If it doesn't exists will throw a FileNotFound exception
try {
    $fs->delete("/home/gonzalo/aaa.txt");
} catch (Exception\FileNotFound  $e) {
    echo $e->getMessage() . "\n";
}
// Now we are going to create a file.
// the second parameter 'true' means if the file doesn't exist will be created. Similar than 'r+'
try {
    $fs->open("/home/gonzalo/aaa.txt", true)
	->write("asasasasasas", "application/octet-stream");
} catch (Exception\FileNotFound $e) {
    echo $e->getMessage() . "\n";
} catch (Exception\WriteError $e) {
    echo $e->getMessage() . "\n";
} catch (Exception $e) {
    echo $e->getMessage() . "\n";
}
// We open the file
$res = $fs->open("/home/gonzalo/aaa.txt");

// we can get the length and the content type
echo $res->getLenght() . "\n";
echo $res->getContentType(). "\n";
// We move it to another location
$to = "/another/location";
$res->move($to);

$res = $fs->open($to);
// we flush the file to the browser
echo $res->raw();

// finally we delete it
$res->delete();
echo "</pre>";

I’ve also created an extra class to allow to dump files from filesystem to CouchDb and vice-versa.

require_once ("Nov/Loader.php");
Nov\Loader::init();
echo "<pre>";
// from filesystem to couchdb
\Nov\CouchDb\Fs\Utils::fs2cdb("/path/from/", NovConf::CDB1);
// from couchdb to filesystem
\Nov\CouchDb\Fs\Utils::cdb2fs(NovConf::CDB1, "/path/to/");
echo "</pre>";

And that’s all. You can download the source code with the examples here. The examples are under document_root/tests/couchdb/ folder. Remember you will need PHP5.3.

 

Source: http://gonzalo123.wordpress.com/2010/08/30/using-couchdb-as-filesystem-with-php/