1 <?php 2 3 if (!class_exists('FastDFS', false)) { 4 define('FDFS_PROTO_PKG_LEN_SIZE', 8); 5 define('FDFS_PROTO_CMD_ACTIVE_TEST', 111); 6 define('FDFS_PROTO_CMD_RESP', 100); 7 define('FDFS_PROTO_CMD_UPLOAD_SLAVE_FILE', 21); 8 define('FDFS_PROTO_CMD_DELETE_FILE', 12); 9 define('FDFS_PROTO_CMD_GET_METADATA', 15); 10 define('FDFS_PROTO_CMD_SET_METADATA', 13); 11 12 //body_length + command + status 13 define('FDFS_HEADER_LENGTH', 10); 14 define('FDFS_IP_ADDRESS_SIZE', 16); 15 define('FDFS_FILE_EXT_NAME_MAX_LEN', 6); 16 define('FDFS_GROUP_NAME_MAX_LEN', 16); 17 define('FDFS_OVERWRITE_METADATA', 1); 18 define('FDFS_MERGE_METADATA', 2); 19 20 // 连接超时时间 21 define('FDFS_CONNECT_TIME_OUT', 5); 22 define('FDFS_FILE_PREFIX_MAX_LEN', 16); 23 24 //传输超时时间 25 define('FDFS_TRANSFER_TIME_OUT', 0); 26 define('FDFS_QUERY_STORE_WITHOUT_GROUP_ONE', 101); 27 define('FDFS_QUERY_STORE_WITH_GROUP_ONE', 104); 28 29 define('FDFS_TRACKER_QUERY_STORAGE_STORE_BODY_LEN', FDFS_GROUP_NAME_MAX_LEN + FDFS_IP_ADDRESS_SIZE + FDFS_PROTO_PKG_LEN_SIZE); 30 31 class FastDFS { 32 33 public $gConfig = array(); 34 35 /** 36 * 37 * @var FastDFSTrackerClient 38 */ 39 private $tracker; 40 41 /** 42 * 43 * @var FastDFSStorageClient 44 */ 45 private $storage; 46 private $error = array( 47 'code' => 0, 48 'msg' => '' 49 ); 50 51 /** 52 * 要使用这个类,你一定要在php的ini文件中进行fastdfs的配置 53 * 54 * @throws FastDFSException 55 */ 56 public function __construct() { 57 $configFile = ''; 58 $ini = parse_ini_file(php_ini_loaded_file()); 59 60 if (!isset($ini['fastdfs_client.tracker_group_count'])) { 61 throw new FastDFSException("no define fastdfs config"); 62 } 63 for ($i = 0; $i < $ini['fastdfs_client.tracker_group_count']; $i++) { 64 if (isset($ini['fastdfs_client.tracker_group' . $i])) { 65 $configFile = $ini['fastdfs_client.tracker_group' . $i]; 66 break; 67 } 68 } 69 if (!file_exists($configFile)) { 70 throw new FastDFSException("client config $configFile not found"); 71 } 72 $this->gConfig = parse_ini_file($configFile); 73 list($this->gConfig['tracker_host'], $this->gConfig['tracker_port']) = explode(':', $this->gConfig['tracker_server']); 74 } 75 76 /** 77 * 获得一个tracker 78 * 79 * @return \FastDFSTrackerClient 80 */ 81 public function tracker_get_connection() { 82 $this->tracker = new FastDFSTrackerClient($this, $this->gConfig['tracker_host'], $this->gConfig['tracker_port']); 83 84 return $this->tracker; 85 } 86 87 /** 88 * 通过tracker获取一个stroage 89 * 90 * @param string $groupName 文件组名,当为空时,组名由tracker决定 91 * @param FastDFSTrackerClient $tracker 92 * @return \FastDFSStorageClient 93 */ 94 public function tracker_query_storage_store($groupName, FastDFSTrackerClient $tracker) { 95 $this->storage = new FastDFSStorageClient($this, $groupName, $tracker); 96 97 return $this->storage; 98 } 99 100 /** 101 * 测试一下tracker服务器是否正常 102 * 103 * @param FastDFSTrackerClient $tracker 104 * @return boolean 105 */ 106 public function active_test(FastDFSTrackerClient $tracker = null) { 107 $this->initTrackerAndStorage($tracker); 108 109 $header = self::packHeader(FDFS_PROTO_CMD_ACTIVE_TEST, 0); 110 $tracker->send($header); 111 112 $resHeader = self::parseHeader($tracker->read(FDFS_HEADER_LENGTH)); 113 114 return $resHeader['status'] == 0 ? true : false; 115 } 116 117 public function get_last_error_no() { 118 return $this->error['code']; 119 } 120 121 public function add_error($errorNo, $info) { 122 $this->error['code'] = $errorNo; 123 $this->error['msg'] = $info; 124 } 125 126 public function get_last_error_info() { 127 return $this->error['msg']; 128 } 129 130 /** 131 * 在storage中删除一个文件 132 * 133 * @param string $groupName 文件所在的组名 134 * @param string $remoteFile 要删除的文件路径 135 * @param FastDFSStorageClient $tracker 136 * @param FastDFSStorageClient $storage 137 */ 138 public function storage_delete_file($groupName, $remoteFile, FastDFSStorageClient $tracker, FastDFSStorageClient $storage) { 139 $this->initTrackerAndStorage($tracker, $storage, $groupName); 140 141 $this->storage->deleteFile($groupName, $remoteFile); 142 } 143 144 /** 145 * 往storage中上传一个文件 146 * 147 * @param string $localFile 你本地的文件路径 148 * @param string $extName 文件的扩展名,当名优提供扩展名时,会自动取文件的扩展名 149 * @param array $metas 文件的附加信息 150 * @param string $groupName 所在的组名,可以为空,为空时,由tracker决定 151 * @param FastDFSTrackerClient $tracker 152 * @param FastDFSStorageClient $storage 153 */ 154 public function storage_upload_by_filename($localFile, $extName = '', $metas = array(), $groupName = '', FastDFSTrackerClient $tracker = null, FastDFSStorageClient $storage = null) { 155 $this->initTrackerAndStorage($tracker, $storage, $groupName); 156 157 return $this->storage->uploadByFilename($localFile, $extName, $metas); 158 } 159 160 /** 161 * 上传一个文件的附属文件,主要使用一个图片有缩略图的情况下 162 * 163 * @param string $localFile 本地文件的路径,缩略图的文件路径 164 * @param string $groupName 组名,最好和主文件在同一个组 165 * @param string $masterFileName 主文件名 166 * @param string $prefix 文件的前缀 167 * @param string $extName 文件的后缀,可以为空,为空时,由tracker决定 168 * @param array $meta 附件信息 169 * @param FastDFSTrackerClient $tracker 170 * @param FastDFSStorageClient $storage 171 */ 172 public function storage_upload_slave_by_filename($localFile, $groupName, $masterFileName, $prefix = '', $extName = '', $meta = array(), FastDFSTrackerClient $tracker = null, FastDFSStorageClient $storage = null) { 173 $this->initTrackerAndStorage($tracker, $storage, $groupName); 174 /*echo $localFile."<br/>".$groupName."<br/>".$masterFileName."<br/>".$prefix; 175 exit;*/ 176 return $this->storage->uploadSalveFile($localFile, $groupName, $masterFileName, $prefix, $extName, $meta); 177 } 178 179 /** 180 * 检查这个文件是否已经存在 181 * 182 * @param string $groupName 文件所在组名 183 * @param string $remoteFile 文件在storage中的名字 184 * @param FastDFSStorageClient $tracker 185 * @param FastDFSStorageClient $storage 186 */ 187 public function storage_file_exist($groupName, $remoteFile, FastDFSTrackerClient $tracker, FastDFSStorageClient $storage) { 188 $this->initTrackerAndStorage($tracker, $storage, $groupName); 189 190 return $this->storage->fileExists($groupName, $remoteFile); 191 } 192 193 public function close() { 194 if ($this->tracker) { 195 $this->tracker->close(); 196 $this->tracker = null; 197 } 198 } 199 200 public function tracker_close_all_connections() { 201 $this->close(); 202 if (!$this->storage) { 203 $this->storage->close(); 204 } 205 } 206 207 public static function padding($str, $len) { 208 209 $str_len = strlen($str); 210 211 return $str_len > $len ? substr($str, 0, $len) : $str . pack('x' . ($len - $str_len)); 212 } 213 214 /** 215 * 216 * @param int $command 217 * @param int $length 218 * @return bytes 219 */ 220 public static function packHeader($command, $length = 0) { 221 return self::packU64($length) . pack('Cx', $command); 222 } 223 224 public static function packMetaData($data) { 225 $S1 = "\x01"; 226 $S2 = "\x02"; 227 228 $list = array(); 229 foreach ($data as $key => $val) { 230 $list[] = $key . $S2 . $val; 231 }; 232 233 return implode($S1, $list); 234 } 235 236 public static function parseMetaData($data) { 237 238 $S1 = "\x01"; 239 $S2 = "\x02"; 240 241 $arr = explode($S1, $data); 242 $result = array(); 243 244 foreach ($arr as $val) { 245 list($k, $v) = explode($S2, $val); 246 $result[$k] = $v; 247 } 248 249 return $result; 250 } 251 252 public static function parseHeader($str, $len = FDFS_HEADER_LENGTH) { 253 254 assert(strlen($str) === $len); 255 256 $result = unpack('C10', $str); 257 258 $length = self::unpackU64(substr($str, 0, 8)); 259 $command = $result[9]; 260 $status = $result[10]; 261 262 return array( 263 'length' => $length, 264 'command' => $command, 265 'status' => $status 266 ); 267 } 268 269 /** 270 * From: sphinxapi.php 271 */ 272 private static function unpackU64($v) { 273 list ( $hi, $lo ) = array_values(unpack("N*N*", $v)); 274 275 if (PHP_INT_SIZE >= 8) { 276 if ($hi < 0) 277 $hi += (1 << 32); // because php 5.2.2 to 5.2.5 is totally fucked up again 278 if ($lo < 0) 279 $lo += (1 << 32); 280 281 // x64, int 282 if ($hi <= 2147483647) 283 return ($hi << 32) + $lo; 284 285 // x64, bcmath 286 if (function_exists("bcmul")) 287 return bcadd($lo, bcmul($hi, "4294967296")); 288 289 // x64, no-bcmath 290 $C = 100000; 291 $h = ((int) ($hi / $C) << 32) + (int) ($lo / $C); 292 $l = (($hi % $C) << 32) + ($lo % $C); 293 if ($l > $C) { 294 $h += (int) ($l / $C); 295 $l = $l % $C; 296 } 297 298 if ($h == 0) 299 return $l; 300 return sprintf("%d%05d", $h, $l); 301 } 302 303 // x32, int 304 if ($hi == 0) { 305 if ($lo > 0) 306 return $lo; 307 return sprintf("%u", $lo); 308 } 309 310 $hi = sprintf("%u", $hi); 311 $lo = sprintf("%u", $lo); 312 313 // x32, bcmath 314 if (function_exists("bcmul")) 315 return bcadd($lo, bcmul($hi, "4294967296")); 316 317 // x32, no-bcmath 318 $hi = (float) $hi; 319 $lo = (float) $lo; 320 321 $q = floor($hi / 10000000.0); 322 $r = $hi - $q * 10000000.0; 323 $m = $lo + $r * 4967296.0; 324 $mq = floor($m / 10000000.0); 325 $l = $m - $mq * 10000000.0; 326 $h = $q * 4294967296.0 + $r * 429.0 + $mq; 327 328 $h = sprintf("%.0f", $h); 329 $l = sprintf("%07.0f", $l); 330 if ($h == "0") 331 return sprintf("%.0f", (float) $l); 332 return $h . $l; 333 } 334 335 private function initTrackerAndStorage(FastDFSTrackerClient $tracker = null, FastDFSStorageClient $storage = null, $groupName = '') { 336 $reNewStorage = false; 337 if ($tracker && $tracker !== $this->tracker) { 338 $this->tracker_get_connection(); 339 } 340 if (($storage && $storage !== $this->storage) || $reNewStorage) { 341 $this->tracker_query_storage_store($groupName, $this->tracker); 342 } 343 } 344 345 /** 346 * From: sphinxapi.php 347 */ 348 public static function packU64($v) { 349 350 351 assert(is_numeric($v)); 352 353 // x64 354 if (PHP_INT_SIZE >= 8) { 355 assert($v >= 0); 356 357 // x64, int 358 if (is_int($v)) 359 return pack("NN", $v >> 32, $v & 0xFFFFFFFF); 360 361 // x64, bcmath 362 if (function_exists("bcmul")) { 363 $h = bcdiv($v, 4294967296, 0); 364 $l = bcmod($v, 4294967296); 365 return pack("NN", $h, $l); 366 } 367 368 // x64, no-bcmath 369 $p = max(0, strlen($v) - 13); 370 $lo = (int) substr($v, $p); 371 $hi = (int) substr($v, 0, $p); 372 373 $m = $lo + $hi * 1316134912; 374 $l = $m % 4294967296; 375 $h = $hi * 2328 + (int) ($m / 4294967296); 376 377 return pack("NN", $h, $l); 378 } 379 380 // x32, int 381 if (is_int($v)) 382 return pack("NN", 0, $v); 383 384 // x32, bcmath 385 if (function_exists("bcmul")) { 386 $h = bcdiv($v, "4294967296", 0); 387 $l = bcmod($v, "4294967296"); 388 return pack("NN", (float) $h, (float) $l); // conversion to float is intentional; int would lose 31st bit 389 } 390 391 // x32, no-bcmath 392 $p = max(0, strlen($v) - 13); 393 $lo = (float) substr($v, $p); 394 $hi = (float) substr($v, 0, $p); 395 396 $m = $lo + $hi * 1316134912.0; 397 $q = floor($m / 4294967296.0); 398 $l = $m - ($q * 4294967296.0); 399 $h = $hi * 2328.0 + $q; 400 401 return pack("NN", $h, $l); 402 } 403 404 } 405 406 abstract class FastDFSBase { 407 408 abstract public function getSocket(); 409 410 abstract public function close(); 411 412 public function read($length, $socket = null) { 413 if (!$socket) { 414 $socket = $this->getSocket(); 415 } 416 417 if (feof($socket)) { 418 throw new FastDFS_Exception('connection unexpectedly closed (timed out?)', $this->_errno); 419 } 420 421 $data = stream_get_contents($socket, $length); 422 423 assert($length === strlen($data)); 424 425 return $data; 426 } 427 428 public function send($data, $length = 0, $socket = null) { 429 if (!$socket) { 430 $socket = $this->getSocket(); 431 } 432 433 if (!$length) { 434 $length = strlen($data); 435 } 436 437 if (feof($socket) || fwrite($socket, $data, $length) !== $length) { 438 throw new Exception('connection unexpectedly closed (timed out?)'); 439 } 440 441 return true; 442 } 443 444 } 445 446 class FastDFSTrackerClient extends FastDFSBase { 447 448 private $host; 449 private $port; 450 451 /** 452 * 453 * @var FastDFS 454 */ 455 private $dfs; 456 private $_socket; 457 458 public function __construct(FastDFS &$dfs, $host, $port) { 459 $this->host = $host; 460 $this->port = $port; 461 $this->dfs = $dfs; 462 463 $this->_socket = @fsockopen("tcp://$host", $port, $errno, $errstr, $this->dfs->gConfig['connect_timeout']); 464 if (!$this->_socket) { 465 $this->dfs->add_error(-2, $errstr); 466 } 467 } 468 469 public function getSocket() { 470 return $this->_socket; 471 } 472 473 public function close() { 474 fclose($this->_socket); 475 } 476 477 } 478 479 class FastDFSStorageClient extends FastDFSBase { 480 481 private $groupName; 482 483 /** 484 * 485 * @var FastDFSTrackerClient 486 */ 487 private $tracker; 488 489 /** 490 * 491 * @var FastDFS 492 */ 493 private $dfs; 494 private $_socket; 495 private $host; 496 private $port; 497 private $storeIndex; 498 499 public function __construct(FastDFS &$dfs, $groupName, FastDFSTrackerClient $tracker) { 500 $this->tracker = $tracker; 501 $this->dfs = $dfs; 502 503 $reqBody = ''; 504 if ($groupName) { 505 $cmd = FDFS_QUERY_STORE_WITH_GROUP_ONE; 506 $len = FDFS_GROUP_NAME_MAX_LEN; 507 $reqBody = FastDFS::padding($groupName, $len); 508 } else { 509 $cmd = FDFS_QUERY_STORE_WITHOUT_GROUP_ONE; 510 $len = 0; 511 } 512 $reqHeader = FastDFS::packHeader($cmd, $len); 513 $this->tracker->send($reqHeader . $reqBody); 514 515 $resHeader = $this->tracker->read(FDFS_HEADER_LENGTH); 516 $resInfo = FastDFS::parseHeader($resHeader); 517 518 if ($resInfo['status'] != 0) { 519 throw new Exception("something wrong with get storage by group name", $resInfo['status']); 520 } 521 522 $resBody = !!$resInfo['length'] ? $this->tracker->read($resInfo['length']) : ''; 523 $this->groupName = trim(substr($resBody, 0, FDFS_GROUP_NAME_MAX_LEN)); 524 $this->host = trim(substr($resBody, FDFS_GROUP_NAME_MAX_LEN, FDFS_IP_ADDRESS_SIZE + 1)); 525 526 list(,, $this->port) = unpack('N2', substr($resBody, FDFS_GROUP_NAME_MAX_LEN + FDFS_IP_ADDRESS_SIZE - 1, FDFS_PROTO_PKG_LEN_SIZE)); 527 528 $this->storeIndex = ord(substr($resBody, -1)); 529 530 $this->_socket = @fsockopen($this->host, $this->port, $errno, $errstr, $this->dfs->gConfig['connect_timeout']); 531 532 if (!$this->_socket) { 533 $this->dfs->add_error($errno, $errstr); 534 } 535 } 536 537 public function getSocket() { 538 return $this->_socket; 539 } 540 541 public function getStorePathIndex() { 542 return $this->storeIndex; 543 } 544 545 public function uploadByFilename($localFile, $extName, $metas) { 546 if (!file_exists($localFile)) { 547 throw new FastDFSException("$localFile file is not exists"); 548 } 549 $pathInfo = pathinfo($localFile); 550 551 $extName = $extName ? $extName : $pathInfo['extension']; 552 $extLen = strlen($extName); 553 554 if ($extLen > FDFS_FILE_EXT_NAME_MAX_LEN) { 555 throw new FastDFSException("file ext too long"); 556 } 557 $fp = fopen($localFile, 'rb'); 558 flock($fp, LOCK_SH); 559 $fileSize = filesize($localFile); 560 561 $reqBodyLen = 1 + FDFS_PROTO_PKG_LEN_SIZE + FDFS_FILE_EXT_NAME_MAX_LEN + $fileSize; 562 $reqHeader = FastDFS::packHeader(11, $reqBodyLen); 563 $reqBody = pack('C', $this->getStorePathIndex()) . FastDFS::packU64($fileSize) . FastDFS::padding($extName, FDFS_FILE_EXT_NAME_MAX_LEN); 564 565 $this->send($reqHeader . $reqBody); 566 567 stream_copy_to_stream($fp, $this->_socket, $fileSize); 568 flock($fp, LOCK_UN); 569 fclose($fp); 570 571 $resHeader = $this->read(FDFS_HEADER_LENGTH); 572 $resInfo = FastDFS::parseHeader($resHeader); 573 574 if ($resInfo['status'] !== 0) { 575 return false; 576 } 577 $resBody = $resInfo['length'] ? $this->read($resInfo['length']) : ''; 578 $groupName = trim(substr($resBody, 0, FDFS_GROUP_NAME_MAX_LEN)); 579 580 $filePath = trim(substr($resBody, FDFS_GROUP_NAME_MAX_LEN)); 581 582 if ($metas) { 583 $this->setFileMetaData($groupName, $filePath, $metas); 584 } 585 586 return array( 587 'group_name' => $groupName, 588 'filename' => $filePath 589 ); 590 } 591 592 /** 593 * 594 * @param type $fileName 595 * @param type $groupName 596 * @param type $masterfile 597 * @param type $prefix 598 * @param type $extName 599 * @param type $metas 600 * @return boolean 601 * @throws FastDFSException 602 */ 603 public function uploadSalveFile($fileName, $groupName, $masterfile, $prefix = '', $extName = '', $metas = array()) { 604 if (!file_exists($fileName)) { 605 throw new FastDFSException("salve file $fileName is not exists"); 606 } 607 608 $pathInfo = pathinfo($fileName); 609 610 $extName = $extName ? $extName : $pathInfo['extension']; 611 $extLen = strlen($extName); 612 613 if ($extLen > FDFS_FILE_EXT_NAME_MAX_LEN) { 614 throw new FastDFSException("salve file ext too long"); 615 } 616 $fp = fopen($fileName, 'rb'); 617 flock($fp, LOCK_SH); 618 619 $fileSize = filesize($fileName); 620 $masterFilePathLen = strlen($masterfile); 621 622 $reqBodyLength = 16 + FDFS_FILE_PREFIX_MAX_LEN + FDFS_FILE_EXT_NAME_MAX_LEN + $masterFilePathLen + $fileSize; 623 $reqHeader = FastDFS::packHeader(FDFS_PROTO_CMD_UPLOAD_SLAVE_FILE, $reqBodyLength); 624 625 $reqBody = pack('x4N', $masterFilePathLen) . FastDFS::packU64($fileSize) . FastDFS::padding($prefix, FDFS_FILE_PREFIX_MAX_LEN); 626 $reqBody .= FastDFS::padding($extName, FDFS_FILE_EXT_NAME_MAX_LEN) . $masterfile; 627 628 $this->send($reqHeader . $reqBody); 629 630 stream_copy_to_stream($fp, $this->_socket, $fileSize); 631 flock($fp, LOCK_UN); 632 fclose($fp); 633 634 $resHeader = $this->read(FDFS_HEADER_LENGTH); 635 $resInfo = FastDFS::parseHeader($resHeader); 636 637 if ($resInfo['status'] !== 0) { 638 return false; 639 } 640 $resBody = $resInfo['length'] ? $this->read($resInfo['length']) : ''; 641 $groupName = trim(substr($resBody, 0, FDFS_GROUP_NAME_MAX_LEN)); 642 643 $filePath = trim(substr($resBody, FDFS_GROUP_NAME_MAX_LEN)); 644 645 if ($metas) { 646 $this->setFileMetaData($groupName, $filePath, $metas); 647 } 648 649 return array( 650 'group_name' => $groupName, 651 'filename' => $filePath 652 ); 653 } 654 655 public function deleteFile($groupName, $fileName) { 656 $reqBodyLen = strlen($fileName) + FDFS_GROUP_NAME_MAX_LEN; 657 $reqHeader = FastDFS::packHeader(FDFS_PROTO_CMD_DELETE_FILE, $reqBodyLen); 658 $reqBody = FastDFS::padding($groupName, FDFS_GROUP_NAME_MAX_LEN) . $fileName; 659 660 $this->send($reqHeader . $reqBody); 661 662 $resHeader = $this->read(FDFS_HEADER_LENGTH); 663 $resInfo = FastDFS::parseHeader($resHeader); 664 665 return !$resInfo['status']; 666 } 667 668 public function fileExists($groupName, $filePath) { 669 $meta = $this->getFileMeta($groupName, $filePath); 670 671 return $meta ? true : false; 672 } 673 674 public function setFileMetaData($groupName, $filePath, array $metaData, $flag = FDFS_OVERWRITE_METADATA) { 675 676 $metaData = FastDFS::packMetaData($metaData); 677 $metaDataLength = strlen($metaData); 678 $filePathLength = strlen($filePath); 679 $flag = $flag === FDFS_OVERWRITE_METADATA ? 'O' : 'M'; 680 681 $reqBodyLength = (FDFS_PROTO_PKG_LEN_SIZE * 2) + 1 + $metaDataLength + $filePathLength + FDFS_GROUP_NAME_MAX_LEN; 682 683 $reqHeader = FastDFS::packHeader(FDFS_PROTO_CMD_SET_METADATA, $reqBodyLength); 684 685 $reqBody = FastDFS::packU64($filePathLength) . FastDFS::packU64($metaDataLength); 686 $reqBody .= $flag . FastDFS::padding($groupName, FDFS_GROUP_NAME_MAX_LEN) . $filePath . $metaData; 687 688 $this->send($reqHeader . $reqBody); 689 690 $resHeader = $this->read(FDFS_HEADER_LENGTH); 691 $resInfo = FastDFS::parseHeader($resHeader); 692 693 return !$resInfo['status']; 694 } 695 696 /** 697 * 取得文件的元信息,如果文件不存在则,返回false,反正是一个关联数组 698 * 699 * @param type $groupName 700 * @param type $filePath 701 * @return boolean 702 */ 703 public function getFileMeta($groupName, $filePath) { 704 $reqBodyLength = strlen($filePath) + FDFS_GROUP_NAME_MAX_LEN; 705 $reqHeader = FastDFS::packHeader(FDFS_PROTO_CMD_GET_METADATA, $reqBodyLength); 706 $reqBody = FastDFS::padding($groupName, FDFS_GROUP_NAME_MAX_LEN) . $filePath; 707 708 $this->send($reqHeader . $reqBody); 709 710 $resHeader = $this->read(FDFS_HEADER_LENGTH); 711 $resInfo = FastDFS::parseHeader($resHeader); 712 713 if (!!$resInfo['status']) { 714 return false; 715 } 716 717 $resBody = $resInfo['length'] ? $this->read($resInfo['length']) : false; 718 719 return FastDFS::parseMetaData($resBody); 720 } 721 722 public function close() { 723 fclose($this->_socket); 724 } 725 726 } 727 728 class FastDFSException extends Exception { 729 730 } 731 732 }
上面是api基类:
下面是调用方式:
1 /** 2 * FastDFS上传文件 3 * @param unknown $fileurl 上传文件 4 * @param unknown $sizes 附属文件 5 * @return string 6 */ 7 private function applydfs($fileurl,$sizes=array()){ 9 $dfs=new FastDFS(); 10 $tracker = $dfs->tracker_get_connection(); 11 $location = ""; 12 if($dfs->active_test($tracker)){ 13 $storaged = $dfs->tracker_query_storage_store("group1",$tracker); 14 if(!empty($sizes)){ 15 $count = 0; 16 $filename = $dfs->storage_upload_by_filename($fileurl); 17 if(isset($filename['group_name'])&&isset($filename['filename'])){ 18 $location =$filename['group_name']."/".$filename['filename']; 19 } 20 foreach($sizes as $key=>$val){ 21 $snapshot_file_info =$dfs->storage_upload_slave_by_filename($val,"group1",$filename['filename'],$key); 22 if($snapshot_file_info){ 23 $count++; 24 } 25 } 26 if($count <> count($sizes)){ 27 $location = ""; 28 } 29 }else{ 30 $filename = $dfs->storage_upload_by_filename($fileurl); 31 if(isset($filename['group_name'])&&isset($filename['filename'])){ 32 $location =$filename['group_name']."/".$filename['filename']; 33 } 34 } 35 } 36 return $location; 37 }
在php.ini中配置:
1 [fastdfs] 2 ; the base path 3 fastdfs_client.base_path = /tmp 4 5 ; connect timeout in seconds 6 ; default value is 30s 7 fastdfs_client.connect_timeout = 2 8 9 ; network timeout in seconds 10 ; default value is 30s 11 fastdfs_client.network_timeout = 60 12 13 ; standard log level as syslog, case insensitive, value list: 14 ;;; emerg for emergency 15 ;;; alert 16 ;;; crit for critical 17 ;;; error 18 ;;; warn for warning 19 ;;; notice 20 ;;; info 21 ;;; debug 22 fastdfs_client.log_level = info 23 24 ; set the log filename, such as /usr/local/fastdfs/logs/fastdfs_client.log 25 ; empty for output to stderr 26 fastdfs_client.log_filename = /tmp/fastdfs_client.log 27 28 ; secret key to generate anti-steal token 29 ; this parameter must be set when http.anti_steal.check_token set to true 30 ; the length of the secret key should not exceed 128 bytes 31 fastdfs_client.http.anti_steal_secret_key = 32 33 ; FastDFS cluster count, default value is 1 34 fastdfs_client.tracker_group_count = 1 35 36 ; config file of FastDFS cluster ;, based 0 37 ; must include absolute path, such as fastdfs_client.tracker_group0 38 ; the config file is same as conf/client.conf 39 ; 这是你fastdfs的tracker的配置文件的路径,必须使用绝对路径,请换成你自己的路径,附件是我们开发环境和测试环境的client.conf配置 40 ; 测试环境已经配置过,你自己的开发环境需要配置一下 41 fastdfs_client.tracker_group0 = "E:/wamp/bin/php/php5.3.13/client.conf" 42 43 ; if use connection pool 44 ; default value is false 45 ; since V4.05 46 fastdfs_client.use_connection_pool = false 47 48 ; connections whose the idle time exceeds this time will be closed 49 ; unit: second 50 ; default value is 3600 51 ; since V4.05 52 fastdfs_client.connection_pool_max_idle_time = 3600 53 54 ; the base path 55 fastdfs_client.base_path = /tmp 56 57 ; connect timeout in seconds 58 ; default value is 30s 59 fastdfs_client.connect_timeout = 2 60 61 ; network timeout in seconds 62 ; default value is 30s 63 fastdfs_client.network_timeout = 60