From 0c9935e01bca86a2e4abba99eb7db447a14d6a7b Mon Sep 17 00:00:00 2001 From: Devon Weller Date: Fri, 18 Nov 2016 12:26:26 -0600 Subject: [PATCH 1/3] Added addWithWrap method. + Use filepath references instead of content when adding files. + Added error handling. + Use new curl_file_create method for file uploading (Requires PHP>=5.5) --- composer.json | 2 +- src/IPFS.php | 121 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 104 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 0dc7a60..7b8a121 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "php": ">=5.3.0", + "php": ">=5.5.0", "ext-curl": "*" }, "autoload": { diff --git a/src/IPFS.php b/src/IPFS.php index 71be34f..2fba7fd 100644 --- a/src/IPFS.php +++ b/src/IPFS.php @@ -6,6 +6,8 @@ namespace Cloutier\PhpIpfsApi; +use Exception; + class IPFS { private $gatewayIP; private $gatewayPort; @@ -24,14 +26,44 @@ public function cat ($hash) { } - public function add ($content) { + /** + * Adds a file directly to IPFS + * @param string $filepath a filepath to upload + * @param boolean $pin pin the file (true) + * @return string The content hash + */ + public function add ($filepath, $pin=true) { + $ip = $this->gatewayIP; + $port = $this->gatewayApiPort; + + $query_vars = [ + 'pin' => ($pin ? 'true' : 'false'), + ]; + + $response = $this->postFile("/api/v0/add", $filepath, $query_vars); + return $response['Hash']; + } + + /** + * Adds a file directly to IPFS + * @param string $filepath a filepath to upload + * @param boolean $pin pin the file (true) + * @return array Data with Name, Hash and FolderHash + */ + public function addWithWrap ($filepath, $pin=true) { $ip = $this->gatewayIP; $port = $this->gatewayApiPort; - $req = $this->curl("http://$ip:$port/api/v0/add?stream-channels=true", $content); - $req = json_decode($req, TRUE); + $query_vars = [ + 'pin' => ($pin ? 'true' : 'false'), + 'w' => 'true', + ]; + + $response = $this->postFile("/api/v0/add", $filepath, $query_vars, $expect_multiple = true); - return $req['Hash']; + $return_data = $response[0]; + $return_data['FolderHash'] = $response[1]['Hash']; + return $return_data; } public function ls ($hash) { @@ -66,16 +98,48 @@ public function pinAdd ($hash) { return $data; } - public function version () { - - $ip = $this->gatewayIP; - $port = $this->gatewayApiPort; - $response = $this->curl("http://$ip:$port/api/v0/version"); - $data = json_decode($response, TRUE); - return $data["Version"]; - } + public function version () { + + $ip = $this->gatewayIP; + $port = $this->gatewayApiPort; + $response = $this->curl("http://$ip:$port/api/v0/version"); + $data = json_decode($response, TRUE); + return $data["Version"]; + } + + public function get($api_path, $query_vars=[]) { + $ip = $this->gatewayIP; + $port = $this->gatewayApiPort; - private function curl ($url, $data = "") { + $query_string = $query_vars ? '?'.http_build_query($query_vars) : ''; + $response = $this->curl("http://{$ip}:{$port}/".ltrim($api_path,'/').$query_string); + $data = json_decode($response, true); + return $data; + } + + public function postFile($api_path, $filepath, $query_vars=[], $expect_multiple=false) { + $ip = $this->gatewayIP; + $port = $this->gatewayApiPort; + + $query_string = $query_vars ? '?'.http_build_query($query_vars) : ''; + $response = $this->curl("http://{$ip}:{$port}/".ltrim($api_path,'/').$query_string, $filepath); + echo "\$response: $response\n"; + + if ($expect_multiple) { + $data = []; + foreach (explode("\n", $response) as $json_data_line) { + if (strlen(trim($json_data_line))) { + $data[] = json_decode($json_data_line, true); + } + } + } else { + $data = json_decode($response, true); + } + + return $data; + } + + private function curl ($url, $filepath=null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); @@ -84,17 +148,38 @@ private function curl ($url, $data = "") { curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); - if ($data != "") { - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data; boundary=a831rwxi1a3gzaorw1w2z49dlsor')); + if ($filepath !== null) { + // add the file + $cfile = curl_file_create($filepath, 'application/octet-stream', basename($filepath)); + + // post + $post_fields = ['file' => $cfile]; curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, "--a831rwxi1a3gzaorw1w2z49dlsor\r\nContent-Type: application/octet-stream\r\nContent-Disposition: file; \r\n\r\n" . $data . " a831rwxi1a3gzaorw1w2z49dlsor"); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields); } $output = curl_exec($ch); - if ($output == FALSE) { - //todo: when ipfs doesn't answer + // check HTTP response code + $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $code_category = substr($response_code, 0, 1); + if ($code_category == '5' OR $code_category == '4') { + $data = @json_decode($output, true); + if (!$data AND json_last_error() != JSON_ERROR_NONE) { + throw new Exception("IPFS returned response code $response_code: ".substr($output, 0, 200), $response_code); + } + if (is_array($data)) { + if (isset($data['Code']) AND isset($data['Message'])) { + throw new Exception("IPFS Error {$data['Code']}: {$data['Message']}", $response_code); + } + } + } + + // handle empty response + if ($output === false) { + throw new Exception("IPFS Error: No Response", 1); } + curl_close($ch); From 609faedcc7e79aed1c9927f873508dbdc1989cee Mon Sep 17 00:00:00 2001 From: Devon Weller Date: Fri, 18 Nov 2016 13:24:32 -0600 Subject: [PATCH 2/3] Added pin remove --- src/IPFS.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/IPFS.php b/src/IPFS.php index 2fba7fd..0d0b3f5 100644 --- a/src/IPFS.php +++ b/src/IPFS.php @@ -98,6 +98,17 @@ public function pinAdd ($hash) { return $data; } + public function pinRemove ($hash) { + + $ip = $this->gatewayIP; + $port = $this->gatewayApiPort; + + $response = $this->curl("http://$ip:$port/api/v0/pin/rm/$hash"); + $data = json_decode($response, TRUE); + + return $data; + } + public function version () { $ip = $this->gatewayIP; From b34576b4c4e2c9c63c8a22a81634f26903b42e0d Mon Sep 17 00:00:00 2001 From: Devon Weller Date: Tue, 20 Dec 2016 07:19:51 -0600 Subject: [PATCH 3/3] Support multiple files in addWithWrap --- src/IPFS.php | 60 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/IPFS.php b/src/IPFS.php index 0d0b3f5..01c6165 100644 --- a/src/IPFS.php +++ b/src/IPFS.php @@ -45,12 +45,27 @@ public function add ($filepath, $pin=true) { } /** - * Adds a file directly to IPFS - * @param string $filepath a filepath to upload + * Adds files to a folder in IPFS + * + * Returns data in the form of: + * { + * "files": [ + * { + * "Name": "brown-square.png", + * "Hash": "Qmb3isYtDpPEAgEBCUBQMU8tqP23h2Rp99zZivWKUmuGMd" + * }, + * { + * "Name": "brown-square.jpg", + * "Hash": "QmZc5W8EXWwRid2djYgG8w2MrDM7CyhHArozF5Ro8C5b8W" + * } + * ], + * "FolderHash": "QmWXmr7sxZHVQjMhY9tPgAuHYrXpxKTjW6MYfaTPVCLLgr" + * } + * @param multi $filepath_or_filepaths a single filepath or an array of filepaths to upload * @param boolean $pin pin the file (true) * @return array Data with Name, Hash and FolderHash */ - public function addWithWrap ($filepath, $pin=true) { + public function addWithWrap ($filepath_or_filepaths, $pin=true) { $ip = $this->gatewayIP; $port = $this->gatewayApiPort; @@ -59,10 +74,23 @@ public function addWithWrap ($filepath, $pin=true) { 'w' => 'true', ]; - $response = $this->postFile("/api/v0/add", $filepath, $query_vars, $expect_multiple = true); + $responses = $this->postFile("/api/v0/add", $filepath_or_filepaths, $query_vars, $expect_multiple = true); + + $return_data = [ + 'files' => [], + 'FolderHash' => '', + ]; + + $responses_count = count($responses); + for ($i=0; $i < $responses_count; $i++) { + if ($i === $responses_count - 1) { + $return_data['FolderHash'] = $responses[$i]['Hash']; + continue; + } + + $return_data['files'][$i] = $responses[$i]; + } - $return_data = $response[0]; - $return_data['FolderHash'] = $response[1]['Hash']; return $return_data; } @@ -134,7 +162,6 @@ public function postFile($api_path, $filepath, $query_vars=[], $expect_multiple= $query_string = $query_vars ? '?'.http_build_query($query_vars) : ''; $response = $this->curl("http://{$ip}:{$port}/".ltrim($api_path,'/').$query_string, $filepath); - echo "\$response: $response\n"; if ($expect_multiple) { $data = []; @@ -150,21 +177,26 @@ public function postFile($api_path, $filepath, $query_vars=[], $expect_multiple= return $data; } - private function curl ($url, $filepath=null) { + private function curl ($url, $filepath_or_filepaths=null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_TIMEOUT, 240); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); - if ($filepath !== null) { - // add the file - $cfile = curl_file_create($filepath, 'application/octet-stream', basename($filepath)); + if ($filepath_or_filepaths !== null) { + $filepaths = $filepath_or_filepaths; + if (!is_array($filepaths)) { $filepaths = [$filepath_or_filepaths]; } + + $post_fields = []; + foreach($filepaths as $offset => $filepath) { + // add the file + $cfile = curl_file_create($filepath, 'application/octet-stream', basename($filepath)); + $post_fields['file'.sprintf('%03d', $offset + 1)] = $cfile; + } - // post - $post_fields = ['file' => $cfile]; curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields); }