├── .gitignore ├── LICENSE ├── README.md ├── conf.inc.php ├── lang └── zh.inc.php ├── lib └── requestcore │ ├── LICENSE │ ├── README.md │ └── requestcore.class.php ├── logs ├── .DS_Store └── logs ├── oss-support.php ├── readme.txt ├── screenshot.jpg ├── sdk.class.php ├── uninstall.php └── util └── mimetypes.class.php /.gitignore: -------------------------------------------------------------------------------- 1 | Git commit –m ".DS_Store remove" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | aliyun-oss-support 2 | ================== 3 | 4 | 使用阿里云存储OSS作为WordPress附件存储空间。This is a plugin for WordPress that uses Aliyun Cloud Storage(OSS) for attachments remote saving. 5 | 6 | 插件地址:http://mawenjian.net/p/977.html 7 | 8 | 同类插件:https://github.com/IvanChou/aliyun-oss-support 9 | 10 | 个人博客:http://mawenjian.net 11 | 12 | ____________________ 13 | 14 | ### 说明:
15 | 1、由于增加了新参数的缘故,所以如果是从v1.0版本升级过来的话,请大家在安装新版本插件后,到配置页面把新的参数补充完整,以免影响使用。
16 | 2、启用插件后,最好将原来上传到WordPress的附件同步到OSS的相应目录,否则在WordPress后台原来上传图片的缩略图会不能显示。
17 | 3、启用插件后,建议在上传文件前规范文件的命名规则,避免因不符合OSS的Object命名规范而导致同步失败。个人建议文件命名使用“26个英文字母”、“数字0-9”以及“-”,除此之外的字符,一律不使用。
18 | 4、启用插件后,对于体积较大的文件,不建议使用WordPress后台上传,因为需要Web服务器进行周转,效率较低,也容易出错(尤其是海外服务器);建议直接通过OSS管理后台或相关工具上传到相应位置。
19 | 5、如果您有任何意见或建议,请到 http://mawenjian.net/p/977.html 提交;
20 | 6、欢迎其他OSS类同步插件将我新加入的功能纳入他们的插件(直接粘贴代码也无所谓,当然,最好可以提及下idea来自于我)。为广大网友提供更好用的插件才是我们的最终目的,其他都不重要。
21 | 22 | ____________________ 23 | 24 | ### 版本号:2.0 beta 25 | 26 | 修正日期:2015-1-5 27 | 28 | #### 修订内容: 29 | 30 | 1、修复了v1.0版本中网友提出的BUG(我能想到的);
31 | 2、更新OSS SDK到最新的 v1.1.6 版本;
32 | 3、修复只能上传图片不能上传其他类型文件的BUG;
33 | 4、支持OSS所有存储地域(杭州、北京、深圳、青岛、香港)和内外网支持;
34 | 5、增加插件启用时的服务器运行环境测试,如果服务器不满足基本要求,则会进行提示;
35 | 6、增加AK/SK/BUCKET校验功能,如果AK/SK没有操作BUCKET的权限,或者BUCKET为“私有”或“公开读写”状态,则会进行相应提示;
36 | 7、增加插件卸载复原功能,会在插件卸载的时候将upload_path_url参数还原;
37 | 8、允许用户选择是否将图片的缩略图不同步到OSS;
38 | 9、优化代码结构,把大部分代码进行了重写,增加了完整的代码注释,对可能产生的错误和可能抛出的异常进行了相应处理,增强了代码的健壮性;
39 | 10、代码同步到了Github( https://github.com/mawenjian/aliyun-oss-support ),方便各位有兴趣的朋友创建新的分支。
40 | 11、完善了插件配置页面的文字描述,即使是小白也能按说明把插件配置好。
41 | 42 | ![github](https://raw.githubusercontent.com/mawenjian/aliyun-oss-support/master/screenshot.jpg "ScreenShot") 43 | -------------------------------------------------------------------------------- /conf.inc.php: -------------------------------------------------------------------------------- 1 | . You can get the code from . 12 | 13 | ### License and Copyright 14 | 15 | This code is Copyright (c) 2008-2010, Ryan Parman. However, I'm licensing this code for others to use under the [Simplified BSD license](http://www.opensource.org/licenses/bsd-license.php). 16 | -------------------------------------------------------------------------------- /lib/requestcore/requestcore.class.php: -------------------------------------------------------------------------------- 1 | ). 91 | */ 92 | public $request_class = 'RequestCore'; 93 | 94 | /** 95 | * The default class to use for HTTP Responses (defaults to ). 96 | */ 97 | public $response_class = 'ResponseCore'; 98 | 99 | /** 100 | * Default useragent string to use. 101 | */ 102 | public $useragent = 'RequestCore/1.4.3'; 103 | 104 | /** 105 | * File to read from while streaming up. 106 | */ 107 | public $read_file = null; 108 | 109 | /** 110 | * The resource to read from while streaming up. 111 | */ 112 | public $read_stream = null; 113 | 114 | /** 115 | * The size of the stream to read from. 116 | */ 117 | public $read_stream_size = null; 118 | 119 | /** 120 | * The length already read from the stream. 121 | */ 122 | public $read_stream_read = 0; 123 | 124 | /** 125 | * File to write to while streaming down. 126 | */ 127 | public $write_file = null; 128 | 129 | /** 130 | * The resource to write to while streaming down. 131 | */ 132 | public $write_stream = null; 133 | 134 | /** 135 | * Stores the intended starting seek position. 136 | */ 137 | public $seek_position = null; 138 | 139 | /** 140 | * The location of the cacert.pem file to use. 141 | */ 142 | public $cacert_location = false; 143 | 144 | /** 145 | * The state of SSL certificate verification. 146 | */ 147 | public $ssl_verification = true; 148 | 149 | /** 150 | * The user-defined callback function to call when a stream is read from. 151 | */ 152 | public $registered_streaming_read_callback = null; 153 | 154 | /** 155 | * The user-defined callback function to call when a stream is written to. 156 | */ 157 | public $registered_streaming_write_callback = null; 158 | 159 | 160 | /*%******************************************************************************************%*/ 161 | // CONSTANTS 162 | 163 | /** 164 | * GET HTTP Method 165 | */ 166 | const HTTP_GET = 'GET'; 167 | 168 | /** 169 | * POST HTTP Method 170 | */ 171 | const HTTP_POST = 'POST'; 172 | 173 | /** 174 | * PUT HTTP Method 175 | */ 176 | const HTTP_PUT = 'PUT'; 177 | 178 | /** 179 | * DELETE HTTP Method 180 | */ 181 | const HTTP_DELETE = 'DELETE'; 182 | 183 | /** 184 | * HEAD HTTP Method 185 | */ 186 | const HTTP_HEAD = 'HEAD'; 187 | 188 | 189 | /*%******************************************************************************************%*/ 190 | // CONSTRUCTOR/DESTRUCTOR 191 | 192 | /** 193 | * Constructs a new instance of this class. 194 | * 195 | * @param string $url (Optional) The URL to request or service endpoint to query. 196 | * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 197 | * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. 198 | * @return $this A reference to the current instance. 199 | */ 200 | public function __construct($url = null, $proxy = null, $helpers = null) 201 | { 202 | // Set some default values. 203 | $this->request_url = $url; 204 | $this->method = self::HTTP_GET; 205 | $this->request_headers = array(); 206 | $this->request_body = ''; 207 | 208 | // Set a new Request class if one was set. 209 | if (isset($helpers['request']) && !empty($helpers['request'])) 210 | { 211 | $this->request_class = $helpers['request']; 212 | } 213 | 214 | // Set a new Request class if one was set. 215 | if (isset($helpers['response']) && !empty($helpers['response'])) 216 | { 217 | $this->response_class = $helpers['response']; 218 | } 219 | 220 | if ($proxy) 221 | { 222 | $this->set_proxy($proxy); 223 | } 224 | 225 | return $this; 226 | } 227 | 228 | /** 229 | * Destructs the instance. Closes opened file handles. 230 | * 231 | * @return $this A reference to the current instance. 232 | */ 233 | public function __destruct() 234 | { 235 | if (isset($this->read_file) && isset($this->read_stream)) 236 | { 237 | fclose($this->read_stream); 238 | } 239 | 240 | if (isset($this->write_file) && isset($this->write_stream)) 241 | { 242 | fclose($this->write_stream); 243 | } 244 | 245 | return $this; 246 | } 247 | 248 | 249 | /*%******************************************************************************************%*/ 250 | // REQUEST METHODS 251 | 252 | /** 253 | * Sets the credentials to use for authentication. 254 | * 255 | * @param string $user (Required) The username to authenticate with. 256 | * @param string $pass (Required) The password to authenticate with. 257 | * @return $this A reference to the current instance. 258 | */ 259 | public function set_credentials($user, $pass) 260 | { 261 | $this->username = $user; 262 | $this->password = $pass; 263 | return $this; 264 | } 265 | 266 | /** 267 | * Adds a custom HTTP header to the cURL request. 268 | * 269 | * @param string $key (Required) The custom HTTP header to set. 270 | * @param mixed $value (Required) The value to assign to the custom HTTP header. 271 | * @return $this A reference to the current instance. 272 | */ 273 | public function add_header($key, $value) 274 | { 275 | $this->request_headers[$key] = $value; 276 | return $this; 277 | } 278 | 279 | /** 280 | * Removes an HTTP header from the cURL request. 281 | * 282 | * @param string $key (Required) The custom HTTP header to set. 283 | * @return $this A reference to the current instance. 284 | */ 285 | public function remove_header($key) 286 | { 287 | if (isset($this->request_headers[$key])) 288 | { 289 | unset($this->request_headers[$key]); 290 | } 291 | return $this; 292 | } 293 | 294 | /** 295 | * Set the method type for the request. 296 | * 297 | * @param string $method (Required) One of the following constants: , , , , . 298 | * @return $this A reference to the current instance. 299 | */ 300 | public function set_method($method) 301 | { 302 | $this->method = strtoupper($method); 303 | return $this; 304 | } 305 | 306 | /** 307 | * Sets a custom useragent string for the class. 308 | * 309 | * @param string $ua (Required) The useragent string to use. 310 | * @return $this A reference to the current instance. 311 | */ 312 | public function set_useragent($ua) 313 | { 314 | $this->useragent = $ua; 315 | return $this; 316 | } 317 | 318 | /** 319 | * Set the body to send in the request. 320 | * 321 | * @param string $body (Required) The textual content to send along in the body of the request. 322 | * @return $this A reference to the current instance. 323 | */ 324 | public function set_body($body) 325 | { 326 | $this->request_body = $body; 327 | return $this; 328 | } 329 | 330 | /** 331 | * Set the URL to make the request to. 332 | * 333 | * @param string $url (Required) The URL to make the request to. 334 | * @return $this A reference to the current instance. 335 | */ 336 | public function set_request_url($url) 337 | { 338 | $this->request_url = $url; 339 | return $this; 340 | } 341 | 342 | /** 343 | * Set additional CURLOPT settings. These will merge with the default settings, and override if 344 | * there is a duplicate. 345 | * 346 | * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. 347 | * @return $this A reference to the current instance. 348 | */ 349 | public function set_curlopts($curlopts) 350 | { 351 | $this->curlopts = $curlopts; 352 | return $this; 353 | } 354 | 355 | /** 356 | * Sets the length in bytes to read from the stream while streaming up. 357 | * 358 | * @param integer $size (Required) The length in bytes to read from the stream. 359 | * @return $this A reference to the current instance. 360 | */ 361 | public function set_read_stream_size($size) 362 | { 363 | $this->read_stream_size = $size; 364 | 365 | return $this; 366 | } 367 | 368 | /** 369 | * Sets the resource to read from while streaming up. Reads the stream from its current position until 370 | * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and 371 | * . 372 | * 373 | * @param resource $resource (Required) The readable resource to read from. 374 | * @param integer $size (Optional) The size of the stream to read. 375 | * @return $this A reference to the current instance. 376 | */ 377 | public function set_read_stream($resource, $size = null) 378 | { 379 | if (!isset($size) || $size < 0) 380 | { 381 | $stats = fstat($resource); 382 | 383 | if ($stats && $stats['size'] >= 0) 384 | { 385 | $position = ftell($resource); 386 | 387 | if ($position !== false && $position >= 0) 388 | { 389 | $size = $stats['size'] - $position; 390 | } 391 | } 392 | } 393 | 394 | $this->read_stream = $resource; 395 | 396 | return $this->set_read_stream_size($size); 397 | } 398 | 399 | /** 400 | * Sets the file to read from while streaming up. 401 | * 402 | * @param string $location (Required) The readable location to read from. 403 | * @return $this A reference to the current instance. 404 | */ 405 | public function set_read_file($location) 406 | { 407 | $this->read_file = $location; 408 | $read_file_handle = fopen($location, 'r'); 409 | 410 | return $this->set_read_stream($read_file_handle); 411 | } 412 | 413 | /** 414 | * Sets the resource to write to while streaming down. 415 | * 416 | * @param resource $resource (Required) The writeable resource to write to. 417 | * @return $this A reference to the current instance. 418 | */ 419 | public function set_write_stream($resource) 420 | { 421 | $this->write_stream = $resource; 422 | 423 | return $this; 424 | } 425 | 426 | /** 427 | * Sets the file to write to while streaming down. 428 | * 429 | * @param string $location (Required) The writeable location to write to. 430 | * @return $this A reference to the current instance. 431 | */ 432 | public function set_write_file($location) 433 | { 434 | $this->write_file = $location; 435 | $write_file_handle = fopen($location, 'w'); 436 | 437 | return $this->set_write_stream($write_file_handle); 438 | } 439 | 440 | /** 441 | * Set the proxy to use for making requests. 442 | * 443 | * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 444 | * @return $this A reference to the current instance. 445 | */ 446 | public function set_proxy($proxy) 447 | { 448 | $proxy = parse_url($proxy); 449 | $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null; 450 | $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null; 451 | $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null; 452 | $this->proxy = $proxy; 453 | return $this; 454 | } 455 | 456 | /** 457 | * Set the intended starting seek position. 458 | * 459 | * @param integer $position (Required) The byte-position of the stream to begin reading from. 460 | * @return $this A reference to the current instance. 461 | */ 462 | public function set_seek_position($position) 463 | { 464 | $this->seek_position = isset($position) ? (integer) $position : null; 465 | 466 | return $this; 467 | } 468 | 469 | /** 470 | * Register a callback function to execute whenever a data stream is read from using 471 | * . 472 | * 473 | * The user-defined callback function should accept three arguments: 474 | * 475 | *
    476 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 477 | *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • 478 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 479 | *
480 | * 481 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    482 | *
  • The name of a global function to execute, passed as a string.
  • 483 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 484 | *
  • An anonymous function (PHP 5.3+).
485 | * @return $this A reference to the current instance. 486 | */ 487 | public function register_streaming_read_callback($callback) 488 | { 489 | $this->registered_streaming_read_callback = $callback; 490 | 491 | return $this; 492 | } 493 | 494 | /** 495 | * Register a callback function to execute whenever a data stream is written to using 496 | * . 497 | * 498 | * The user-defined callback function should accept two arguments: 499 | * 500 | *
    501 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 502 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 503 | *
504 | * 505 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    506 | *
  • The name of a global function to execute, passed as a string.
  • 507 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 508 | *
  • An anonymous function (PHP 5.3+).
509 | * @return $this A reference to the current instance. 510 | */ 511 | public function register_streaming_write_callback($callback) 512 | { 513 | $this->registered_streaming_write_callback = $callback; 514 | 515 | return $this; 516 | } 517 | 518 | 519 | /*%******************************************************************************************%*/ 520 | // PREPARE, SEND, AND PROCESS REQUEST 521 | 522 | /** 523 | * A callback function that is invoked by cURL for streaming up. 524 | * 525 | * @param resource $curl_handle (Required) The cURL handle for the request. 526 | * @param resource $file_handle (Required) The open file handle resource. 527 | * @param integer $length (Required) The maximum number of bytes to read. 528 | * @return binary Binary data from a stream. 529 | */ 530 | public function streaming_read_callback($curl_handle, $file_handle, $length) 531 | { 532 | // Once we've sent as much as we're supposed to send... 533 | if ($this->read_stream_read >= $this->read_stream_size) 534 | { 535 | // Send EOF 536 | return ''; 537 | } 538 | 539 | // If we're at the beginning of an upload and need to seek... 540 | if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) 541 | { 542 | if (fseek($this->read_stream, $this->seek_position) !== 0) 543 | { 544 | throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.'); 545 | } 546 | } 547 | 548 | $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size 549 | $this->read_stream_read += strlen($read); 550 | 551 | $out = $read === false ? '' : $read; 552 | 553 | // Execute callback function 554 | if ($this->registered_streaming_read_callback) 555 | { 556 | call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out); 557 | } 558 | 559 | return $out; 560 | } 561 | 562 | /** 563 | * A callback function that is invoked by cURL for streaming down. 564 | * 565 | * @param resource $curl_handle (Required) The cURL handle for the request. 566 | * @param binary $data (Required) The data to write. 567 | * @return integer The number of bytes written. 568 | */ 569 | public function streaming_write_callback($curl_handle, $data) 570 | { 571 | $length = strlen($data); 572 | $written_total = 0; 573 | $written_last = 0; 574 | 575 | while ($written_total < $length) 576 | { 577 | $written_last = fwrite($this->write_stream, substr($data, $written_total)); 578 | 579 | if ($written_last === false) 580 | { 581 | return $written_total; 582 | } 583 | 584 | $written_total += $written_last; 585 | } 586 | 587 | // Execute callback function 588 | if ($this->registered_streaming_write_callback) 589 | { 590 | call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total); 591 | } 592 | 593 | return $written_total; 594 | } 595 | 596 | /** 597 | * Prepares and adds the details of the cURL request. This can be passed along to a 598 | * function. 599 | * 600 | * @return resource The handle for the cURL object. 601 | */ 602 | public function prep_request() 603 | { 604 | $curl_handle = curl_init(); 605 | 606 | // Set default options. 607 | curl_setopt($curl_handle, CURLOPT_URL, $this->request_url); 608 | curl_setopt($curl_handle, CURLOPT_FILETIME, true); 609 | curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false); 610 | curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED); 611 | curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5); 612 | curl_setopt($curl_handle, CURLOPT_HEADER, true); 613 | curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true); 614 | curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000); 615 | curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120); 616 | curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true); 617 | curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url); 618 | curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent); 619 | curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback')); 620 | 621 | // Verification of the SSL cert 622 | if ($this->ssl_verification) 623 | { 624 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true); 625 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, true); 626 | } 627 | else 628 | { 629 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false); 630 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false); 631 | } 632 | 633 | // chmod the file as 0755 634 | if ($this->cacert_location === true) 635 | { 636 | curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem'); 637 | } 638 | elseif (is_string($this->cacert_location)) 639 | { 640 | curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location); 641 | } 642 | 643 | // Debug mode 644 | if ($this->debug_mode) 645 | { 646 | curl_setopt($curl_handle, CURLOPT_VERBOSE, true); 647 | } 648 | 649 | // Handle open_basedir & safe mode 650 | if (!ini_get('safe_mode') && !ini_get('open_basedir')) 651 | { 652 | curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); 653 | } 654 | 655 | // Enable a proxy connection if requested. 656 | if ($this->proxy) 657 | { 658 | curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true); 659 | 660 | $host = $this->proxy['host']; 661 | $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : ''; 662 | curl_setopt($curl_handle, CURLOPT_PROXY, $host); 663 | 664 | if (isset($this->proxy['user']) && isset($this->proxy['pass'])) 665 | { 666 | curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); 667 | } 668 | } 669 | 670 | // Set credentials for HTTP Basic/Digest Authentication. 671 | if ($this->username && $this->password) 672 | { 673 | curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); 674 | curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password); 675 | } 676 | 677 | // Handle the encoding if we can. 678 | if (extension_loaded('zlib')) 679 | { 680 | curl_setopt($curl_handle, CURLOPT_ENCODING, ''); 681 | } 682 | 683 | // Process custom headers 684 | if (isset($this->request_headers) && count($this->request_headers)) 685 | { 686 | $temp_headers = array(); 687 | 688 | foreach ($this->request_headers as $k => $v) 689 | { 690 | $temp_headers[] = $k . ': ' . $v; 691 | } 692 | 693 | curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers); 694 | } 695 | 696 | switch ($this->method) 697 | { 698 | case self::HTTP_PUT: 699 | //unset($this->read_stream); 700 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT'); 701 | if (isset($this->read_stream)) 702 | { 703 | if (!isset($this->read_stream_size) || $this->read_stream_size < 0) 704 | { 705 | throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); 706 | } 707 | 708 | curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); 709 | curl_setopt($curl_handle, CURLOPT_UPLOAD, true); 710 | } 711 | else 712 | { 713 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 714 | } 715 | break; 716 | 717 | case self::HTTP_POST: 718 | curl_setopt($curl_handle, CURLOPT_POST, true); 719 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 720 | break; 721 | 722 | case self::HTTP_HEAD: 723 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD); 724 | curl_setopt($curl_handle, CURLOPT_NOBODY, 1); 725 | break; 726 | 727 | default: // Assumed GET 728 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method); 729 | if (isset($this->write_stream)) 730 | { 731 | curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback')); 732 | curl_setopt($curl_handle, CURLOPT_HEADER, false); 733 | } 734 | else 735 | { 736 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 737 | } 738 | break; 739 | } 740 | 741 | // Merge in the CURLOPTs 742 | if (isset($this->curlopts) && sizeof($this->curlopts) > 0) 743 | { 744 | foreach ($this->curlopts as $k => $v) 745 | { 746 | curl_setopt($curl_handle, $k, $v); 747 | } 748 | } 749 | 750 | return $curl_handle; 751 | } 752 | 753 | /** 754 | * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the 755 | * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via 756 | * parameters. 757 | * 758 | * @param resource $curl_handle (Optional) The reference to the already executed cURL request. 759 | * @param string $response (Optional) The actual response content itself that needs to be parsed. 760 | * @return ResponseCore A object containing a parsed HTTP response. 761 | */ 762 | public function process_response($curl_handle = null, $response = null) 763 | { 764 | // Accept a custom one if it's passed. 765 | if ($curl_handle && $response) 766 | { 767 | $this->curl_handle = $curl_handle; 768 | $this->response = $response; 769 | } 770 | 771 | // As long as this came back as a valid resource... 772 | if (is_resource($this->curl_handle)) 773 | { 774 | // Determine what's what. 775 | $header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE); 776 | $this->response_headers = substr($this->response, 0, $header_size); 777 | $this->response_body = substr($this->response, $header_size); 778 | $this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE); 779 | $this->response_info = curl_getinfo($this->curl_handle); 780 | 781 | // Parse out the headers 782 | $this->response_headers = explode("\r\n\r\n", trim($this->response_headers)); 783 | $this->response_headers = array_pop($this->response_headers); 784 | $this->response_headers = explode("\r\n", $this->response_headers); 785 | array_shift($this->response_headers); 786 | 787 | // Loop through and split up the headers. 788 | $header_assoc = array(); 789 | foreach ($this->response_headers as $header) 790 | { 791 | $kv = explode(': ', $header); 792 | $header_assoc[strtolower($kv[0])] = isset($kv[1])?$kv[1]:''; 793 | } 794 | 795 | // Reset the headers to the appropriate property. 796 | $this->response_headers = $header_assoc; 797 | $this->response_headers['_info'] = $this->response_info; 798 | $this->response_headers['_info']['method'] = $this->method; 799 | 800 | if ($curl_handle && $response) 801 | { 802 | return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle); 803 | } 804 | } 805 | 806 | // Return false 807 | return false; 808 | } 809 | 810 | /** 811 | * Sends the request, calling necessary utility functions to update built-in properties. 812 | * 813 | * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not. 814 | * @return string The resulting unparsed data from the request. 815 | */ 816 | public function send_request($parse = false) 817 | { 818 | //set_time_limit(0); 819 | 820 | $curl_handle = $this->prep_request(); 821 | $this->response = curl_exec($curl_handle); 822 | 823 | if ($this->response === false) 824 | { 825 | throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')'); 826 | } 827 | 828 | $parsed_response = $this->process_response($curl_handle, $this->response); 829 | 830 | curl_close($curl_handle); 831 | 832 | if ($parse) 833 | { 834 | return $parsed_response; 835 | } 836 | 837 | return $this->response; 838 | } 839 | 840 | /** 841 | * Sends the request using , enabling parallel requests. Uses the "rolling" method. 842 | * 843 | * @param array $handles (Required) An indexed array of cURL handles to process simultaneously. 844 | * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    845 | *
  • callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
  • 846 | *
  • limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
847 | * @return array Post-processed cURL responses. 848 | */ 849 | public function send_multi_request($handles, $opt = null) 850 | { 851 | //set_time_limit(0); 852 | 853 | // Skip everything if there are no handles to process. 854 | if (count($handles) === 0) return array(); 855 | 856 | if (!$opt) $opt = array(); 857 | 858 | // Initialize any missing options 859 | $limit = isset($opt['limit']) ? $opt['limit'] : -1; 860 | 861 | // Initialize 862 | $handle_list = $handles; 863 | $http = new $this->request_class(); 864 | $multi_handle = curl_multi_init(); 865 | $handles_post = array(); 866 | $added = count($handles); 867 | $last_handle = null; 868 | $count = 0; 869 | $i = 0; 870 | 871 | // Loop through the cURL handles and add as many as it set by the limit parameter. 872 | while ($i < $added) 873 | { 874 | if ($limit > 0 && $i >= $limit) break; 875 | curl_multi_add_handle($multi_handle, array_shift($handles)); 876 | $i++; 877 | } 878 | 879 | do 880 | { 881 | $active = false; 882 | 883 | // Start executing and wait for a response. 884 | while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM) 885 | { 886 | // Start looking for possible responses immediately when we have to add more handles 887 | if (count($handles) > 0) break; 888 | } 889 | 890 | // Figure out which requests finished. 891 | $to_process = array(); 892 | 893 | while ($done = curl_multi_info_read($multi_handle)) 894 | { 895 | // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html ) 896 | if ($done['result'] > 0) 897 | { 898 | throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')'); 899 | } 900 | 901 | // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests 902 | elseif (!isset($to_process[(int) $done['handle']])) 903 | { 904 | $to_process[(int) $done['handle']] = $done; 905 | } 906 | } 907 | 908 | // Actually deal with the request 909 | foreach ($to_process as $pkey => $done) 910 | { 911 | $response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle'])); 912 | $key = array_search($done['handle'], $handle_list, true); 913 | $handles_post[$key] = $response; 914 | 915 | if (count($handles) > 0) 916 | { 917 | curl_multi_add_handle($multi_handle, array_shift($handles)); 918 | } 919 | 920 | curl_multi_remove_handle($multi_handle, $done['handle']); 921 | curl_close($done['handle']); 922 | } 923 | } 924 | while ($active || count($handles_post) < $added); 925 | 926 | curl_multi_close($multi_handle); 927 | 928 | ksort($handles_post, SORT_NUMERIC); 929 | return $handles_post; 930 | } 931 | 932 | 933 | /*%******************************************************************************************%*/ 934 | // RESPONSE METHODS 935 | 936 | /** 937 | * Get the HTTP response headers from the request. 938 | * 939 | * @param string $header (Optional) A specific header value to return. Defaults to all headers. 940 | * @return string|array All or selected header values. 941 | */ 942 | public function get_response_header($header = null) 943 | { 944 | if ($header) 945 | { 946 | return $this->response_headers[strtolower($header)]; 947 | } 948 | return $this->response_headers; 949 | } 950 | 951 | /** 952 | * Get the HTTP response body from the request. 953 | * 954 | * @return string The response body. 955 | */ 956 | public function get_response_body() 957 | { 958 | return $this->response_body; 959 | } 960 | 961 | /** 962 | * Get the HTTP response code from the request. 963 | * 964 | * @return string The HTTP response code. 965 | */ 966 | public function get_response_code() 967 | { 968 | return $this->response_code; 969 | } 970 | } 971 | 972 | 973 | /** 974 | * Container for all response-related methods. 975 | */ 976 | class ResponseCore 977 | { 978 | /** 979 | * Stores the HTTP header information. 980 | */ 981 | public $header; 982 | 983 | /** 984 | * Stores the SimpleXML response. 985 | */ 986 | public $body; 987 | 988 | /** 989 | * Stores the HTTP response code. 990 | */ 991 | public $status; 992 | 993 | /** 994 | * Constructs a new instance of this class. 995 | * 996 | * @param array $header (Required) Associative array of HTTP headers (typically returned by ). 997 | * @param string $body (Required) XML-formatted response from AWS. 998 | * @param integer $status (Optional) HTTP response status code from the request. 999 | * @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. 1000 | */ 1001 | public function __construct($header, $body, $status = null) 1002 | { 1003 | $this->header = $header; 1004 | $this->body = $body; 1005 | $this->status = $status; 1006 | 1007 | return $this; 1008 | } 1009 | 1010 | /** 1011 | * Did we receive the status code we expected? 1012 | * 1013 | * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. 1014 | * @return boolean Whether we received the expected status code or not. 1015 | */ 1016 | public function isOK($codes = array(200, 201, 204, 206)) 1017 | { 1018 | if (is_array($codes)) 1019 | { 1020 | return in_array($this->status, $codes); 1021 | } 1022 | 1023 | return $this->status === $codes; 1024 | } 1025 | } 1026 | 1027 | /** 1028 | * Default RequestCore Exception. 1029 | */ 1030 | class RequestCore_Exception extends Exception {} 1031 | -------------------------------------------------------------------------------- /logs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawenjian/aliyun-oss-support/24f004f4565021b957763cc6949374328b1b5485/logs/.DS_Store -------------------------------------------------------------------------------- /logs/logs: -------------------------------------------------------------------------------- 1 | This is log folder, it cannot be remove. -------------------------------------------------------------------------------- /oss-support.php: -------------------------------------------------------------------------------- 1 | "", 33 | 'ak' => "", 34 | 'sk' => "", 35 | 'host' => "oss.aliyuncs.com", 36 | 'nothumb' => "false", 37 | 'nolocalsaving' => "false", 38 | 'upload_url_path' => "", 39 | 40 | ); 41 | 42 | add_option('oss_options', $options, '', 'yes'); 43 | } 44 | 45 | /** 46 | * 服务器运行环境测试 47 | */ 48 | function test_server_env() { 49 | try { 50 | //实例化存储对象 51 | $test_ak = 'hello'; 52 | $test_sk = 'world'; 53 | $test_bucket = 'imgs-storage'; 54 | $aliyun_oss = new ALIOSS($test_ak, $test_sk); 55 | $oss_option = array(ALIOSS::OSS_CONTENT_TYPE => 'text/xml'); 56 | $response = $aliyun_oss->get_bucket_acl($test_bucket, $oss_option); 57 | } catch(Exception $ex) { 58 | echo " 59 |

注意:测试结果显示,Aliyun OSS Support插件似乎不能在本服务器上正常运行...

60 | ".$ex->getMessage(); 61 | } 62 | } 63 | 64 | function oss_admin_warnings() { 65 | $oss_options = get_option('oss_options', TRUE); 66 | 67 | $oss_bucket = attribute_escape($oss_options['bucket']); 68 | if ( !$oss_options['bucket'] && !isset($_POST['submit']) ) { 69 | function oss_warning() { 70 | echo " 71 |

".__('OSS is almost ready.')." ".sprintf(__('You must enter your OSS Bucket for it to work.'), "options-general.php?page=" . OSS_BASEFOLDER . "/oss-support.php")."

72 | "; 73 | //执行服务器运行环境测试 74 | test_server_env(); 75 | } 76 | add_action('admin_notices', 'oss_warning'); 77 | return; 78 | } 79 | } 80 | oss_admin_warnings(); 81 | 82 | /** 83 | *上传函数 84 | *@param $object 85 | *@param $file 86 | *@param $opt 87 | *@return bool 88 | */ 89 | function _file_upload( $object , $file , $opt = array()){ 90 | //设置超时时间 91 | //set_time_limit(120); 92 | 93 | //如果文件不存在,直接返回FALSE 94 | if( !@file_exists($file) ) 95 | return FALSE; 96 | 97 | //获取WP配置信息 98 | $oss_options = get_option('oss_options', TRUE); 99 | $oss_bucket = attribute_escape($oss_options['bucket']); 100 | $oss_ak = attribute_escape($oss_options['ak']); 101 | $oss_sk = attribute_escape($oss_options['sk']); 102 | $oss_host = attribute_escape($oss_options['host']); 103 | if($oss_host==null || $oss_host == '') 104 | $oss_host = 'oss.aliyuncs.com'; 105 | 106 | if(@file_exists($file)) { 107 | try { 108 | //实例化存储对象 109 | if(!is_object($aliyun_oss)) 110 | $aliyun_oss = new ALIOSS($oss_ak, $oss_sk ,$oss_host); 111 | //上传原始文件,$opt暂时没有使用 112 | $aliyun_oss->upload_file_by_file( $oss_bucket, $object, $file, $opt ); 113 | 114 | return TRUE; 115 | 116 | } catch(Exception $ex) { 117 | return FALSE; 118 | } 119 | 120 | } else { 121 | return FALSE; 122 | } 123 | } 124 | 125 | 126 | /** 127 | * 是否需要删除本地文件 128 | * @return bool 129 | */ 130 | function _is_delete_local_file() { 131 | $oss_options = get_option('oss_options', TRUE); 132 | return (attribute_escape($oss_options['nolocalsaving'])=='true'); 133 | } 134 | 135 | /** 136 | * 删除本地文件 137 | * 138 | * @param $file 本地文件路径 139 | * @return bool 140 | */ 141 | function _delete_local_file($file){ 142 | try{ 143 | //文件不存在 144 | if(!@file_exists($file)) 145 | return TRUE; 146 | //删除文件 147 | if(!@unlink($file)) 148 | return FALSE; 149 | return TRUE; 150 | } 151 | catch(Exception $ex){ 152 | return FALSE; 153 | } 154 | } 155 | 156 | 157 | /** 158 | * 将文件按内容写入OSS 159 | * @param $uploadpath 存放路径 160 | * @param $content 写入的内容 161 | * @return bool 162 | */ 163 | function _file_upload_by_contents($uploadpath, $content, $type) { 164 | //获取WP配置信息 165 | $oss_options = get_option('oss_options', TRUE); 166 | $oss_bucket = attribute_escape($oss_options['bucket']); 167 | $oss_ak = attribute_escape($oss_options['ak']); 168 | $oss_sk = attribute_escape($oss_options['sk']); 169 | $oss_host = attribute_escape($oss_options['host']); 170 | if($oss_host==null || $oss_host == '') 171 | $oss_host = 'oss.aliyuncs.com'; 172 | 173 | try { 174 | //实例化存储对象 175 | if(!is_object($aliyun_oss)) 176 | $aliyun_oss = new ALIOSS($oss_ak, $oss_sk ,$oss_host); 177 | //上传原始文件,$opt暂时没有使用 178 | $upload_file_options = array( 179 | 'content' => $content, 180 | 'length' => strlen($content), 181 | ALIOSS::OSS_HEADERS => array( 182 | //'Expires' => '2012-10-01 08:00:00', 183 | ), 184 | ALIOSS::OSS_CONTENT_TYPE => $type, 185 | ); 186 | $aliyun_oss->upload_file_by_content( $oss_bucket, $uploadpath, $upload_file_options ); 187 | 188 | return TRUE; 189 | 190 | } catch(Exception $ex) { 191 | return FALSE; 192 | } 193 | } 194 | 195 | /** 196 | * 判断是否为上传插件/主题等系统操作 197 | */ 198 | function _is_system_op() { 199 | if ($_GET["action"] == 'upload-plugin' || $_GET["action"] == 'upload-theme') { 200 | return true; 201 | } else { 202 | return false; 203 | } 204 | } 205 | 206 | /** 207 | * 上传附件(包括图片的原图) 208 | * @param $metadata 209 | * @return array() 210 | */ 211 | function upload_attachments($metadata) { 212 | //避免上传插件/主题时出现同步到OSS的情况 213 | if(_is_system_op()) { 214 | return $metadata; 215 | } 216 | 217 | $wp_uploads = wp_upload_dir(); 218 | //生成object在OSS中的存储路径 219 | if(get_option('upload_path') == '.') { 220 | //如果含有“./”则去除之 221 | $metadata['file'] = str_replace("./" ,'' ,$metadata['file']); 222 | } 223 | $object = str_replace(get_home_path(), '', $metadata['file']); 224 | 225 | //在本地的存储路径 226 | //$file = $metadata['file']; 227 | $file = get_home_path().$object; //向上兼容,较早的WordPress版本上$metadata['file']存放的是相对路径 228 | 229 | //设置可选参数 230 | $opt =array('Content-Type' => $metadata['type']); 231 | 232 | //执行上传操作 233 | _file_upload ( $object, $file, $opt); 234 | 235 | //如果不在本地保存,则删除本地文件 236 | //if( _is_delete_local_file() ){ 237 | // _delete_local_file($file); 238 | //} 239 | 240 | return $metadata; 241 | } 242 | add_filter('wp_handle_upload', 'upload_attachments', 999999); 243 | 244 | /** 245 | * 上传图片的缩略图 246 | * @param $metadata 247 | * @return array 248 | */ 249 | function upload_thumbs($metadata) { 250 | if(_is_system_op()) { 251 | return $metadata; 252 | } 253 | 254 | //上传所有缩略图 255 | if (isset($metadata['sizes']) && count($metadata['sizes']) > 0) 256 | { 257 | //获取OSS插件的配置信息 258 | $oss_options = get_option('oss_options', TRUE); 259 | //是否需要上传缩略图(已废弃) 260 | //$nothumb = (attribute_escape($oss_options['nothumb']) == 'true'); 261 | //是否需要删除本地文件(已废弃) 262 | //$is_delete_local_file = (attribute_escape($oss_options['nolocalsaving'])=='true'); 263 | 264 | //获取上传路径 265 | $wp_uploads = wp_upload_dir(); 266 | //得到本地文件夹和远端文件夹 267 | $file_path = $wp_uploads['path'].'/'; 268 | if(get_option('upload_path') == '.') { 269 | $file_path = str_replace("./" ,'' , $file_path); 270 | } 271 | 272 | //there may be duplicated filenames,so .... 273 | foreach ($metadata['sizes'] as $val) 274 | { 275 | //生成本地存储路径 276 | $file = $file_path . $val['file']; 277 | 278 | //如果不在本地保存,则删除(已废弃) 279 | //if($is_delete_local_file) { 280 | // _delete_local_file($file); 281 | //} 282 | } 283 | } 284 | 285 | return $metadata; 286 | } 287 | add_filter('wp_generate_attachment_metadata', 'upload_thumbs', 999999); 288 | 289 | 290 | /** 291 | * 修改缩略图的Meta Data 292 | * @param $metadata 293 | * @return $metadata 294 | */ 295 | function modefiy_img_meta($metadata) { 296 | $filename = basename($metadata['file']); 297 | if(isset($metadata['sizes']['thumbnail'])) { 298 | $metadata['sizes']['thumbnail']['file'] = $filename.'@!thumbnail'; 299 | } 300 | if(isset($metadata['sizes']['post-thumbnail'])) { 301 | $metadata['sizes']['post-thumbnail']['file'] = $filename.'@!post-thumbnail'; 302 | } 303 | if(isset($metadata['sizes']['medium'])) { 304 | $metadata['sizes']['medium']['file'] = $filename.'@!medium'; 305 | } 306 | if(isset($metadata['sizes']['large'])) { 307 | $metadata['sizes']['large']['file'] = $filename.'@!large'; 308 | } 309 | return $metadata; 310 | } 311 | add_filter('wp_get_attachment_metadata', 'modefiy_img_meta', 999999); 312 | 313 | 314 | //hook所有xmlrpc的上传 315 | function xml_to_oss($methods) { 316 | $methods['wp.uploadFile'] = 'oss_xmlrpc_upload'; 317 | $methods['metaWeblog.newMediaObject'] = 'oss_xmlrpc_upload'; 318 | return $methods; 319 | } 320 | function oss_xmlrpc_upload($args){ 321 | $data = $args[3]; 322 | $object = sanitize_file_name( $data['name'] ); 323 | $type = $data['type']; 324 | $contents = $data['bits']; 325 | 326 | $wp_upload_dir = wp_upload_dir(); 327 | $upload_url_path = get_option('upload_url_path'); 328 | $object_path = $wp_upload_dir['url'].'/'.$object; 329 | $url = $upload_url_path.'/'.$object; 330 | _file_upload_by_contents($object_path, $contents, $type); 331 | 332 | return array( 'file' => $url, 'url' => $url, 'type' => $type ); 333 | } 334 | add_filter( 'xmlrpc_methods', 'xml_to_oss'); 335 | 336 | 337 | /** 338 | * 删除远程服务器上的单个文件 339 | * @static 340 | * @param $file 341 | * @return void 342 | */ 343 | function delete_remote_file($file) 344 | { 345 | 346 | //获取WP配置信息 347 | $oss_options = get_option('oss_options', TRUE); 348 | $oss_bucket = attribute_escape($oss_options['bucket']); 349 | $oss_ak = attribute_escape($oss_options['ak']); 350 | $oss_sk = attribute_escape($oss_options['sk']); 351 | $oss_host = attribute_escape($oss_options['host']); 352 | if($oss_host==null || $oss_host == '') 353 | $oss_host = 'oss.aliyuncs.com'; 354 | 355 | //得到远端路径 356 | $del_file_path = str_replace(get_home_path(), '', $file); 357 | //_logged('2delete.txt',"del_file_path=$del_file_path"); 358 | try{ 359 | //实例化存储对象 360 | if(!is_object($aliyun_oss)) 361 | $aliyun_oss = new ALIOSS($oss_ak, $oss_sk, $oss_host); 362 | //删除文件 363 | $aliyun_oss->delete_object( $oss_bucket, $del_file_path); 364 | } catch(Exception $ex){} 365 | 366 | return $file; 367 | } 368 | add_action('wp_delete_file', 'delete_remote_file', 999999); 369 | 370 | /** 371 | * 当upload_path为根目录时,需要移除URL中出现的“绝对路径” 372 | */ 373 | function modefiy_img_url($url, $post_id) { 374 | $home_path = get_home_path(); 375 | $url = str_replace($home_path ,'' ,$url); 376 | //_logged('2modify_url.txt',"url=$url"); 377 | return $url; 378 | } 379 | if(get_option('upload_path') == '.') { 380 | add_filter('wp_get_attachment_url', 'modefiy_img_url', 30, 2); 381 | } 382 | 383 | function oss_plugin_action_links( $links, $file ) { 384 | if ( $file == plugin_basename( dirname(__FILE__).'/oss-support.php' ) ) { 385 | $links[] = ''.__('Settings').''; 386 | } 387 | 388 | return $links; 389 | } 390 | 391 | add_filter( 'plugin_action_links', 'oss_plugin_action_links', 10, 2 ); 392 | 393 | function oss_add_setting_page() { 394 | add_options_page('OSS Setting', 'OSS Setting', 8, __FILE__, 'oss_setting_page'); 395 | } 396 | 397 | add_action('admin_menu', 'oss_add_setting_page'); 398 | 399 | function oss_setting_page() { 400 | 401 | $options = array(); 402 | if($_POST['bucket']) { 403 | $options['bucket'] = trim(stripslashes($_POST['bucket'])); 404 | } 405 | if($_POST['ak']) { 406 | $options['ak'] = trim(stripslashes($_POST['ak'])); 407 | } 408 | if($_POST['sk']) { 409 | $options['sk'] = trim(stripslashes($_POST['sk'])); 410 | } 411 | if($_POST['host']) { 412 | $options['host'] = trim(stripslashes($_POST['host'])); 413 | } 414 | if($_POST['nothumb']) { 415 | $options['nothumb'] = (isset($_POST['nothumb']))?'true':'false'; 416 | } 417 | if($_POST['nolocalsaving']) { 418 | $options['nolocalsaving'] = (isset($_POST['nolocalsaving']))?'true':'false'; 419 | } 420 | if($_POST['upload_url_path']) { 421 | //仅用于插件卸载时比较使用 422 | $options['upload_url_path'] = trim(stripslashes($_POST['upload_url_path'])); 423 | } 424 | 425 | //检查提交的AK/SK是否有管理该bucket的权限 426 | $flag = 0; 427 | if($_POST['bucket']&&$_POST['ak']&&$_POST['sk']){ 428 | try{ 429 | if(!is_object($aliyun_oss)) 430 | $aliyun_oss = new ALIOSS( $options['ak'], $options['sk'], $options['host']); 431 | $oss_option = array(ALIOSS::OSS_CONTENT_TYPE => 'text/xml'); 432 | $response = $aliyun_oss->get_bucket_acl($options['bucket'],$oss_option); 433 | if($response->status == 200) { 434 | $flag = 1; 435 | if( preg_match('/public-read-write<\/Grant>/i',$response->body) > 0 ) { 436 | $flag = -11; 437 | } elseif( preg_match('/private<\/Grant>/i',$response->body) > 0 ) { 438 | $flag = -12; 439 | } 440 | } elseif ($response->status == 403 && preg_match('//i',$response->body) > 0) { 441 | $flag = -2; 442 | } 443 | 444 | } catch(Exception $ex){ 445 | $flag = -1; 446 | } 447 | } 448 | 449 | if($options !== array() ){ 450 | //更新数据库 451 | update_option('oss_options', $options); 452 | 453 | $upload_path = trim(trim(stripslashes($_POST['upload_path'])),'/'); 454 | $upload_path = ($upload_path == '') ? ('wp-content/uploads') : ($upload_path); 455 | update_option('upload_path', $upload_path ); 456 | 457 | $upload_url_path = trim(trim(stripslashes($_POST['upload_url_path'])),'/'); 458 | update_option('upload_url_path', $upload_url_path ); 459 | 460 | ?> 461 |

设置已保存! 462 | 注意:您的AK/SK没有管理该Bucket的权限,因此不能正常使用!'; 465 | elseif($flag == -1) 466 | echo '注意:网络通信错误,未能校验您的AK/SK是否对该bucket是否具有管理权限'; 467 | elseif($flag == -11) 468 | echo '注意:该BUCKET现在处于“公开读写”状态,会有安全隐患哦!设置成“公开读”就足够了。'; 469 | elseif($flag == -12) 470 | echo '注意:该BUCKET现在处于“私有”状态,不能被其他人访问哦!建议将BUKET权限设置成“公开读”。'; 471 | elseif($flag == -2) 472 | echo '注意:该BUCKET的“存储地域”或“HOST主机”可能搞错了,请再次确认下。'; 473 | ?> 474 |

475 | 499 |
500 |

阿里云附件 v2.1 设置

501 |
502 | 503 | 504 | 505 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 520 | 521 | 522 | 523 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 594 | 595 | 596 | 597 | 604 | 605 | 606 | 607 | 608 | 609 |
Bucket 设置 506 | 507 |

请先访问 阿里云存储 创建 bucket ,再填写以上内容。

508 |
Access Key
Secret Key 517 | 518 |

访问 阿里云 密钥管理页面,获取 AK/SK

519 |
HOST主机 524 | 551 | 552 |

默认 杭州节点(外网) ,其他节点需要更改。【查看详情】

553 | 574 |
不上传缩略图 disabled="true" />
不在本地保留备份 disabled="true" />
本地文件夹: 591 | 592 |

附件在服务器上的存储位置,例如: wp-content/uploads (注意不要以“/”开头和结尾),根目录请输入.

593 |
URL前缀: 598 | 599 |

注意:

600 |

1)URL前缀的格式为 http://{OSS域名} (“本地文件夹”为 . 时),或者 http://{OSS域名}/{本地文件夹} ,“本地文件夹”务必与上面保持一致(结尾无 / )。

601 |

2)OSS中的存放路径(即“文件夹”)与上述 本地文件夹 中定义的路径是相同的(出于方便切换考虑)。

602 |

3)如果需要使用 独立域名 ,直接将 {OSS域名} 替换为 独立域名 即可。

603 |
更新选项
610 |
611 |
612 | 615 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === 阿里云附件 === 2 | Contributors: 马文建(Wenjian Ma) 3 | Donate link: http://mawenjian.net/977.html 4 | Tags: attachment, aliyun, manager, images, thumbnail 5 | License: GPLv2 or later 6 | Requires at least: 1.5 7 | Tested up to: 4.1 8 | Stable tag: 2.0 9 | 10 | 使用阿里云存储OSS作为附件存储空间。 11 | This is a plugin that uses Aliyun Cloud Storage(Aliyun OSS) for attachments remote saving. 12 | 13 | == Description == 14 | 15 | 该插件支持使用阿里云存储OSS作为附件存储空间。 16 | This is a plugin that uses Aliyun Cloud Storage(Aliyun OSS) for attachments remote saving. 17 | 18 | == Installation == 19 | 20 | 1. 下载,解压缩并上传到WordPress插件目录 21 | 2. 在插件管理后台激活插件 22 | 3. 工具 > OSS Support 23 | 24 | 1. Download, unzip and upload to your WordPress plugins directory 25 | 2. activate the plugin within you WordPress Administration Backend 26 | 3. Go to Tools > OSS Support 27 | 28 | == Changelog == 29 | 30 | = 2.0 = 31 | * 更新OSS SDK到 v1.1.6 版本 32 | * 修复只能上传图片不能上传其他类型文件的BUG; 33 | * 支持OSS所有存储地域(杭州、北京、深圳、青岛、香港); 34 | * 增加启用插件时服务器运行环境测试; 35 | * 增加AK/SK/BUCKET校验功能,如果AK/SK没有操作BUCKET的权限,或者BUCKET为“私有”或“公开读写”状态,则会进行提示; 36 | * 增加插件卸载时upload_path_url复原功能; 37 | * 允许缩略图不同步到OSS; 38 | * 优化代码,把大部分代码进行了重写,增加了代码注释; 39 | * 代码同步到了Github(https://github.com/mawenjian/aliyun-oss-support),方便各位创建新的分支。 40 | * 完善文字描述; 41 | 42 | = 1.0 = 43 | * 实现插件原型 44 | 45 | == Upgrade Notice == 46 | 47 | = 2.0 = 48 | 进行了十分重要的更新 49 | 50 | 51 | == Frequently Asked Questions == 52 | * 暂无 53 | 54 | == Screenshots == 55 | * 暂无 -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawenjian/aliyun-oss-support/24f004f4565021b957763cc6949374328b1b5485/screenshot.jpg -------------------------------------------------------------------------------- /sdk.class.php: -------------------------------------------------------------------------------- 1 | access_id = $access_id; 284 | $this->access_key = $access_key; 285 | }elseif (defined('OSS_ACCESS_ID') && defined('OSS_ACCESS_KEY')){ 286 | $this->access_id = OSS_ACCESS_ID; 287 | $this->access_key = OSS_ACCESS_KEY; 288 | }else{ 289 | throw new OSS_Exception(NOT_SET_OSS_ACCESS_ID_AND_ACCESS_KEY); 290 | } 291 | 292 | //校验access_id&access_key 293 | if(empty($this->access_id) || empty($this->access_key)){ 294 | throw new OSS_Exception(OSS_ACCESS_ID_OR_ACCESS_KEY_EMPTY); 295 | } 296 | 297 | //校验hostname 298 | if(NULL === $hostname){ 299 | $this->hostname = self::DEFAULT_OSS_HOST; 300 | }else{ 301 | $this->hostname = $hostname; 302 | } 303 | } 304 | 305 | 306 | /*%******************************************************************************************************%*/ 307 | //属性 308 | 309 | /** 310 | * 设置debug模式 311 | * @param boolean $debug_mode (Optional) 312 | * @author xiaobing.meng@alibaba-inc.com 313 | * @since 2012-05-29 314 | * @return void 315 | */ 316 | public function set_debug_mode($debug_mode = true){ 317 | $this->debug_mode = $debug_mode; 318 | } 319 | 320 | /** 321 | * 设置最大尝试次数 322 | * @param int $max_retries 323 | * @author xiaobing.meng@alibaba-inc.com 324 | * @since 2012-05-29 325 | * @return void 326 | */ 327 | public function set_max_retries($max_retries = 3){ 328 | $this->max_retries = $max_retries; 329 | } 330 | 331 | /** 332 | * 获取最大尝试次数 333 | * @author xiaobing.meng@alibaba-inc.com 334 | * @since 2012-05-29 335 | * @return int 336 | */ 337 | public function get_max_retries(){ 338 | return $this->max_retries; 339 | } 340 | 341 | /** 342 | * 设置host地址 343 | * @author xiaobing.meng@alibaba-inc.com 344 | * @param string $hostname host name 345 | * @param int $port int 346 | * @since 2012-06-11 347 | * @return void 348 | */ 349 | public function set_host_name($hostname, $port = null){ 350 | $this->hostname = $hostname; 351 | 352 | if($port){ 353 | $this->port = $port; 354 | $this->hostname .= ':'.$port; 355 | } 356 | } 357 | 358 | /** 359 | * 设置vhost地址 360 | * @author xiaobing.meng@alibaba-inc.com 361 | * @param string $vhost vhost 362 | * @since 2012-06-11 363 | * @return void 364 | */ 365 | public function set_vhost($vhost){ 366 | $this->vhost = $vhost; 367 | } 368 | 369 | /** 370 | * 设置路径形式,如果为true,则启用三级域名,如bucket.oss.aliyuncs.com 371 | * @author xiaobing.meng@alibaba-inc.com 372 | * @param boolean $enable_domain_style 373 | * @since 2012-06-11 374 | * @return void 375 | */ 376 | public function set_enable_domain_style($enable_domain_style = true){ 377 | $this->enable_domain_style = $enable_domain_style; 378 | } 379 | 380 | 381 | /*%******************************************************************************************************%*/ 382 | //请求 383 | 384 | /** 385 | * Authorization 386 | * @param array $options (Required) 387 | * @throws OSS_Exception 388 | * @author xiaobing.meng@alibaba-inc.com 389 | * @since 2012-05-31 390 | */ 391 | public function auth($options){ 392 | //开始记录LOG 393 | $msg = "---LOG START---------------------------------------------------------------------------\n"; 394 | 395 | //验证Bucket,list_bucket时不需要验证 396 | if(!( ('/' == $options[self::OSS_OBJECT]) && ('' == $options[self::OSS_BUCKET]) && ('GET' == $options[self::OSS_METHOD])) && !$this->validate_bucket($options[self::OSS_BUCKET])){ 397 | throw new OSS_Exception('"'.$options[self::OSS_BUCKET].'"'.OSS_BUCKET_NAME_INVALID); 398 | } 399 | 400 | //验证Object 401 | if(isset($options[self::OSS_OBJECT]) && !$this->validate_object($options[self::OSS_OBJECT])){ 402 | throw new OSS_Exception($options[self::OSS_OBJECT].OSS_OBJECT_NAME_INVALID); 403 | } 404 | 405 | //Object编码为UTF-8 406 | if($this->is_gb2312($options[self::OSS_OBJECT])){ 407 | $options[self::OSS_OBJECT] = iconv('GB2312', "UTF-8",$options[self::OSS_OBJECT]); 408 | }elseif($this->check_char($options[self::OSS_OBJECT],true)){ 409 | $options[self::OSS_OBJECT] = iconv('GBK', "UTF-8",$options[self::OSS_OBJECT]); 410 | } 411 | 412 | 413 | //验证ACL 414 | if(isset($options[self::OSS_HEADERS][self::OSS_ACL]) && !empty($options[self::OSS_HEADERS][self::OSS_ACL])){ 415 | if(!in_array(strtolower($options[self::OSS_HEADERS][self::OSS_ACL]), self::$OSS_ACL_TYPES)){ 416 | throw new OSS_Exception($options[self::OSS_HEADERS][self::OSS_ACL].':'.OSS_ACL_INVALID); 417 | } 418 | } 419 | 420 | 421 | //定义scheme 422 | $scheme = $this->use_ssl ? 'https://' : 'http://'; 423 | 424 | if($this->enable_domain_style){ 425 | $hostname = $this->vhost ? $this->vhost : (($options[self::OSS_BUCKET] =='')?$this->hostname:($options[self::OSS_BUCKET].'.').$this->hostname); 426 | }else{ 427 | $hostname = (isset($options[self::OSS_BUCKET]) && ''!==$options[self::OSS_BUCKET])?$this->hostname.'/'.$options[self::OSS_BUCKET]:$this->hostname; 428 | } 429 | 430 | 431 | //请求参数 432 | $resource = ''; 433 | $sub_resource = ''; 434 | $signable_resource = ''; 435 | $query_string_params = array(); 436 | $signable_query_string_params = array(); 437 | $string_to_sign = ''; 438 | 439 | $headers = array ( 440 | self::OSS_CONTENT_MD5 => '', 441 | self::OSS_CONTENT_TYPE => isset($options[self::OSS_CONTENT_TYPE])?$options[self::OSS_CONTENT_TYPE]:'application/x-www-form-urlencoded', 442 | self::OSS_DATE => isset($options[self::OSS_DATE])? $options[self::OSS_DATE]: gmdate('D, d M Y H:i:s \G\M\T'), 443 | self::OSS_HOST => $this->enable_domain_style?$hostname:$this->hostname, 444 | ); 445 | 446 | if(isset ( $options [self::OSS_OBJECT] ) && '/' !== $options [self::OSS_OBJECT]){ 447 | //$options[self::OSS_OBJECT] = $this->replace_invalid_xml_char($options[self::OSS_OBJECT]); 448 | $signable_resource = '/'.str_replace(array('%2F','%25'),array('/','%'), rawurlencode($options[self::OSS_OBJECT])); 449 | } 450 | 451 | if(isset($options[self::OSS_QUERY_STRING])){ 452 | $query_string_params = array_merge($query_string_params,$options[self::OSS_QUERY_STRING]); 453 | } 454 | $query_string = $this->to_query_string($query_string_params); 455 | 456 | $signable_list = array( 457 | 'partNumber', 458 | 'uploadId', 459 | ); 460 | 461 | foreach ($signable_list as $item){ 462 | if(isset($options[$item])){ 463 | $signable_query_string_params[$item] = $options[$item]; 464 | } 465 | } 466 | $signable_query_string = $this->to_query_string($signable_query_string_params); 467 | 468 | //合并 HTTP headers 469 | if (isset ( $options [self::OSS_HEADERS] )) { 470 | $headers = array_merge ( $headers, $options [self::OSS_HEADERS] ); 471 | } 472 | 473 | //生成请求URL 474 | $conjunction = '?'; 475 | 476 | $non_signable_resource = ''; 477 | 478 | if (isset($options[self::OSS_SUB_RESOURCE])){ 479 | $signable_resource .= $conjunction . $options[self::OSS_SUB_RESOURCE]; 480 | $conjunction = '&'; 481 | } 482 | 483 | if($signable_query_string !== ''){ 484 | $signable_query_string = $conjunction.$signable_query_string; 485 | $conjunction = '&'; 486 | } 487 | 488 | if($query_string !== ''){ 489 | $non_signable_resource .= $conjunction . $query_string; 490 | $conjunction = '&'; 491 | } 492 | 493 | $this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource; 494 | 495 | $msg .= "--REQUEST URL:----------------------------------------------\n".$this->request_url."\n"; 496 | 497 | //创建请求 498 | $request = new RequestCore($this->request_url); 499 | 500 | // Streaming uploads 501 | if (isset($options[self::OSS_FILE_UPLOAD])){ 502 | if (is_resource($options[self::OSS_FILE_UPLOAD])){ 503 | $length = null; 504 | 505 | if (isset($options[self::OSS_CONTENT_LENGTH])){ 506 | $length = $options[self::OSS_CONTENT_LENGTH]; 507 | }elseif (isset($options[self::OSS_SEEK_TO])){ 508 | 509 | $stats = fstat($options[self::OSS_FILE_UPLOAD]); 510 | 511 | if ($stats && $stats[self::OSS_SIZE] >= 0){ 512 | $length = $stats[self::OSS_SIZE] - (integer) $options[self::OSS_SEEK_TO]; 513 | } 514 | } 515 | 516 | $request->set_read_stream($options[self::OSS_FILE_UPLOAD], $length); 517 | 518 | if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded'){ 519 | $headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream'; 520 | } 521 | }else{ 522 | $request->set_read_file($options[self::OSS_FILE_UPLOAD]); 523 | 524 | $length = $request->read_stream_size; 525 | 526 | if (isset($options[self::OSS_CONTENT_LENGTH])){ 527 | $length = $options[self::OSS_CONTENT_LENGTH]; 528 | }elseif (isset($options[self::OSS_SEEK_TO]) && isset($length)){ 529 | $length -= (integer) $options[self::OSS_SEEK_TO]; 530 | } 531 | 532 | $request->set_read_stream_size($length); 533 | 534 | if (isset($headers[self::OSS_CONTENT_TYPE]) && ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded')){ 535 | $extension = explode('.', $options[self::OSS_FILE_UPLOAD]); 536 | $extension = array_pop($extension); 537 | $mime_type = MimeTypes::get_mimetype($extension); 538 | $headers[self::OSS_CONTENT_TYPE] = $mime_type; 539 | } 540 | } 541 | 542 | $options[self::OSS_CONTENT_MD5] = ''; 543 | } 544 | 545 | if (isset($options[self::OSS_SEEK_TO])){ 546 | $request->set_seek_position((integer) $options[self::OSS_SEEK_TO]); 547 | } 548 | 549 | if (isset($options[self::OSS_FILE_DOWNLOAD])){ 550 | if (is_resource($options[self::OSS_FILE_DOWNLOAD])){ 551 | $request->set_write_stream($options[self::OSS_FILE_DOWNLOAD]); 552 | }else{ 553 | $request->set_write_file($options[self::OSS_FILE_DOWNLOAD]); 554 | } 555 | } 556 | 557 | 558 | if(isset($options[self::OSS_METHOD])){ 559 | $request->set_method($options[self::OSS_METHOD]); 560 | $string_to_sign .= $options[self::OSS_METHOD] . "\n"; 561 | } 562 | 563 | if (isset ( $options [self::OSS_CONTENT] )) { 564 | $request->set_body ( $options [self::OSS_CONTENT] ); 565 | if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded'){ 566 | $headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream'; 567 | } 568 | 569 | $headers[self::OSS_CONTENT_LENGTH] = strlen($options [self::OSS_CONTENT]); 570 | $headers[self::OSS_CONTENT_MD5] = $this->hex_to_base64(md5($options[self::OSS_CONTENT])); 571 | } 572 | 573 | uksort($headers, 'strnatcasecmp'); 574 | 575 | foreach ( $headers as $header_key => $header_value ) { 576 | $header_value = str_replace ( array ("\r", "\n" ), '', $header_value ); 577 | if ($header_value !== '') { 578 | $request->add_header ( $header_key, $header_value ); 579 | } 580 | 581 | if ( 582 | strtolower($header_key) === 'content-md5' || 583 | strtolower($header_key) === 'content-type' || 584 | strtolower($header_key) === 'date' || 585 | (isset($options['self::OSS_PREAUTH']) && (integer) $options['self::OSS_PREAUTH'] > 0) 586 | ){ 587 | $string_to_sign .= $header_value . "\n"; 588 | }elseif (substr(strtolower($header_key), 0, 6) === self::OSS_DEFAULT_PREFIX){ 589 | $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; 590 | } 591 | } 592 | 593 | $string_to_sign .= '/' . $options[self::OSS_BUCKET]; 594 | $string_to_sign .= $this->enable_domain_style ? ($options[self::OSS_BUCKET]!=''? ($options[self::OSS_OBJECT]=='/'?'/':'') :'' ) : ''; 595 | $string_to_sign .= rawurldecode($signable_resource) . urldecode($signable_query_string); 596 | 597 | $msg .= "STRING TO SIGN:----------------------------------------------\n".$string_to_sign."\n"; 598 | 599 | $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->access_key, true)); 600 | $request->add_header('Authorization', 'OSS ' . $this->access_id . ':' . $signature); 601 | 602 | if (isset($options[self::OSS_PREAUTH]) && (integer) $options[self::OSS_PREAUTH] > 0){ 603 | return $this->request_url . $conjunction . self::OSS_URL_ACCESS_KEY_ID.'=' . $this->access_id . '&'.self::OSS_URL_EXPIRES.'=' . $options[self::OSS_PREAUTH] . '&'.self::OSS_URL_SIGNATURE.'=' . rawurlencode($signature); 604 | }elseif (isset($options[self::OSS_PREAUTH])){ 605 | return $this->request_url; 606 | } 607 | 608 | if ($this->debug_mode){ 609 | $request->debug_mode = $this->debug_mode; 610 | } 611 | 612 | $msg .= "REQUEST HEADERS:----------------------------------------------\n".serialize($request->request_headers)."\n"; 613 | 614 | $request->send_request(); 615 | 616 | $response_header = $request->get_response_header(); 617 | $response_header['x-oss-request-url'] = $this->request_url; 618 | $response_header['x-oss-redirects'] = $this->redirects; 619 | $response_header['x-oss-stringtosign'] = $string_to_sign; 620 | $response_header['x-oss-requestheaders'] = $request->request_headers; 621 | 622 | $msg .= "RESPONSE HEADERS:----------------------------------------------\n".serialize($response_header)."\n"; 623 | 624 | $data = new ResponseCore ( $response_header , $request->get_response_body (), $request->get_response_code () ); 625 | 626 | if((integer)$request->get_response_code() === 400 /*Bad Request*/ || (integer)$request->get_response_code() === 500 /*Internal Error*/ || (integer)$request->get_response_code() === 503 /*Service Unavailable*/){ 627 | if($this->redirects <= $this->max_retries ){ 628 | //设置休眠 629 | $delay = (integer) (pow(4, $this->redirects) * 100000); 630 | usleep($delay); 631 | $this->redirects++; 632 | $data = $this->auth($options); 633 | } 634 | } 635 | 636 | $msg .= "RESPONSE DATA:----------------------------------------------\n".serialize($data)."\n"; 637 | $msg .= date('Y-m-d H:i:s').":---LOG END---------------------------------------------------------------------------\n"; 638 | //add log 639 | $this->log($msg); 640 | 641 | $this->redirects = 0; 642 | return $data; 643 | } 644 | 645 | 646 | /*%******************************************************************************************************%*/ 647 | //Service Operation 648 | 649 | /** 650 | * Get Bucket list 651 | * @param array $options (Optional) 652 | * @throws OSS_Exception 653 | * @author xiaobing.meng@alibaba-inc.com 654 | * @since 2011-11-14 655 | * @return ResponseCore 656 | */ 657 | public function list_bucket($options = NULL) { 658 | //$options 659 | $this->validate_options($options); 660 | 661 | if (! $options) { 662 | $options = array (); 663 | } 664 | 665 | $options[self::OSS_BUCKET] = ''; 666 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 667 | $options[self::OSS_OBJECT] = '/'; 668 | $response = $this->auth ( $options ); 669 | 670 | return $response; 671 | } 672 | 673 | 674 | /*%******************************************************************************************************%*/ 675 | //Bucket Operation 676 | 677 | /** 678 | * Create Bucket 679 | * @param string $bucket (Required) 680 | * @param string $acl (Optional) 681 | * @param array $options (Optional) 682 | * @author xiaobing.meng@alibaba-inc.com 683 | * @since 2011-11-14 684 | * @return ResponseCore 685 | */ 686 | public function create_bucket($bucket, $acl = self::OSS_ACL_TYPE_PRIVATE, $options = NULL){ 687 | //$options 688 | $this->validate_options($options); 689 | 690 | if (! $options) { 691 | $options = array (); 692 | } 693 | 694 | //bucket 695 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 696 | 697 | $options[self::OSS_BUCKET] = $bucket; 698 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 699 | $options[self::OSS_OBJECT] = '/'; 700 | $options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl); 701 | $response = $this->auth ( $options ); 702 | 703 | return $response; 704 | } 705 | 706 | /** 707 | * Delete Bucket 708 | * @param string $bucket (Required) 709 | * @param array $options (Optional) 710 | * @author xiaobing.meng@alibaba-inc.com 711 | * @since 2011-11-14 712 | * @return ResponseCore 713 | */ 714 | public function delete_bucket($bucket, $options = NULL){ 715 | //$options 716 | $this->validate_options($options); 717 | 718 | if (! $options) { 719 | $options = array (); 720 | } 721 | 722 | //bucket 723 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 724 | 725 | $options[self::OSS_BUCKET] = $bucket; 726 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 727 | $options[self::OSS_OBJECT] = '/'; 728 | $response = $this->auth ( $options ); 729 | 730 | return $response; 731 | } 732 | 733 | /** 734 | * Get Bucket's ACL 735 | * @param string $bucket (Required) 736 | * @param array $options (Optional) 737 | * @throws OSS_Exception 738 | * @author xiaobing.meng@alibaba-inc.com 739 | * @since 2011-11-14 740 | * @return ResponseCore 741 | */ 742 | public function get_bucket_acl($bucket, $options = NULL){ 743 | //options 744 | $this->validate_options($options); 745 | 746 | if(!$options){ 747 | $options = array(); 748 | } 749 | 750 | //bucket 751 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 752 | 753 | $options[self::OSS_BUCKET] = $bucket; 754 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 755 | $options[self::OSS_OBJECT] = '/'; 756 | $options[self::OSS_SUB_RESOURCE] = 'acl'; 757 | $response = $this->auth ( $options ); 758 | 759 | return $response; 760 | } 761 | 762 | /** 763 | * Set Bucket'ACL 764 | * @param string $bucket (Required) 765 | * @param string $acl (Required) 766 | * @param array $options (Optional) 767 | * @throws OSS_Exception 768 | * @author xiaobing.meng@alibaba-inc.com 769 | * @since 2011-11-14 770 | * @return ResponseCore 771 | */ 772 | public function set_bucket_acl($bucket, $acl, $options = NULL){ 773 | //options 774 | $this->validate_options($options); 775 | 776 | if(!$options){ 777 | $options = array(); 778 | } 779 | 780 | //bucket 781 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 782 | 783 | $options[self::OSS_BUCKET] = $bucket; 784 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 785 | $options[self::OSS_OBJECT] = '/'; 786 | $options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl); 787 | $response = $this->auth ( $options ); 788 | 789 | return $response; 790 | } 791 | 792 | /** 793 | * Get Bucket's Logging 794 | * @param string $bucket (Required) 795 | * @param array $options (Optional) 796 | * @throws OSS_Exception 797 | * @author lijie.ma@alibaba-inc.com 798 | * @since 2014-05-04 799 | * @return ResponseCore 800 | */ 801 | public function get_bucket_logging($bucket, $options = NULL){ 802 | //options 803 | $this->validate_options($options); 804 | 805 | if(!$options){ 806 | $options = array(); 807 | } 808 | 809 | //bucket 810 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 811 | 812 | $options[self::OSS_BUCKET] = $bucket; 813 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 814 | $options[self::OSS_OBJECT] = '/'; 815 | $options[self::OSS_SUB_RESOURCE] = 'logging'; 816 | $response = $this->auth ($options); 817 | 818 | return $response; 819 | } 820 | 821 | /** 822 | * Set Bucket's Logging 823 | * @param string $bucket (Required) 824 | * @param string $target_bucket (Required) 825 | * @param string $target_prefix (Optional) 826 | * @param array $options (Optional) 827 | * @throws OSS_Exception 828 | * @author lijie.ma@alibaba-inc.com 829 | * @since 2014-05-04 830 | * @return ResponseCore 831 | */ 832 | public function set_bucket_logging($bucket, $target_bucket, $target_prefix, $options = NULL){ 833 | //options 834 | $this->validate_options($options); 835 | 836 | if(!$options){ 837 | $options = array(); 838 | } 839 | 840 | //bucket 841 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 842 | $this->is_empty($target_bucket,OSS_TARGET_BUCKET_IS_NOT_ALLOWED_EMPTY); 843 | 844 | $options[self::OSS_BUCKET] = $bucket; 845 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 846 | $options[self::OSS_OBJECT] = '/'; 847 | $options[self::OSS_SUB_RESOURCE] = 'logging'; 848 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; 849 | 850 | $xml = new SimpleXMLElement(''); 851 | $logging_enabled_part=$xml->addChild('LoggingEnabled'); 852 | $logging_enabled_part->addChild('TargetBucket', $target_bucket); 853 | $logging_enabled_part->addChild('TargetPrefix', $target_prefix); 854 | 855 | $options[self::OSS_CONTENT] = $xml->asXML(); 856 | echo $xml->asXML(); 857 | return $this->auth($options); 858 | } 859 | 860 | /** 861 | * Delete Bucket's Logging 862 | * @param string $bucket (Required) 863 | * @param array $options (Optional) 864 | * @throws OSS_Exception 865 | * @author lijie.ma@alibaba-inc.com 866 | * @since 2014-05-04 867 | * @return ResponseCore 868 | */ 869 | public function delete_bucket_logging($bucket, $options = NULL){ 870 | //options 871 | $this->validate_options($options); 872 | 873 | if(!$options){ 874 | $options = array(); 875 | } 876 | 877 | //bucket 878 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 879 | 880 | $options[self::OSS_BUCKET] = $bucket; 881 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 882 | $options[self::OSS_OBJECT] = '/'; 883 | $options[self::OSS_SUB_RESOURCE] = 'logging'; 884 | $response = $this->auth ($options); 885 | 886 | return $response; 887 | } 888 | 889 | /** 890 | * Set Bucket's Website 891 | * @param string $bucket (Required) 892 | * @param string $index_document (Required) 893 | * @param string $error_document (Optional) 894 | * @param array $options (Optional) 895 | * @throws OSS_Exception 896 | * @author lijie.ma@alibaba-inc.com 897 | * @since 2014-05-04 898 | * @return ResponseCore 899 | */ 900 | public function set_bucket_website($bucket, $index_document, $error_document, $options = NULL){ 901 | //options 902 | $this->validate_options($options); 903 | 904 | if(!$options){ 905 | $options = array(); 906 | } 907 | 908 | //bucket 909 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 910 | $this->is_empty($index_document,OSS_INDEX_DOCUMENT_IS_NOT_ALLOWED_EMPTY); 911 | 912 | $options[self::OSS_BUCKET] = $bucket; 913 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 914 | $options[self::OSS_OBJECT] = '/'; 915 | $options[self::OSS_SUB_RESOURCE] = 'website'; 916 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; 917 | 918 | $xml = new SimpleXMLElement(''); 919 | $index_document_part=$xml->addChild('IndexDocument'); 920 | $error_document_part=$xml->addChild('ErrorDocument'); 921 | 922 | $index_document_part->addChild('Suffix', $index_document); 923 | $error_document_part->addChild('Key', $error_document); 924 | 925 | $options[self::OSS_CONTENT] = $xml->asXML(); 926 | echo $xml->asXML(); 927 | return $this->auth($options); 928 | } 929 | 930 | /** 931 | * Get Bucket's Website 932 | * @param string $bucket (Required) 933 | * @param array $options (Optional) 934 | * @throws OSS_Exception 935 | * @author lijie.ma@alibaba-inc.com 936 | * @since 2014-05-04 937 | * @return ResponseCore 938 | */ 939 | public function get_bucket_website($bucket, $options = NULL){ 940 | //options 941 | $this->validate_options($options); 942 | 943 | if(!$options){ 944 | $options = array(); 945 | } 946 | 947 | //bucket 948 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 949 | 950 | $options[self::OSS_BUCKET] = $bucket; 951 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 952 | $options[self::OSS_OBJECT] = '/'; 953 | $options[self::OSS_SUB_RESOURCE] = 'website'; 954 | $response = $this->auth ($options); 955 | 956 | return $response; 957 | } 958 | 959 | /** 960 | * Delete Bucket's Website 961 | * @param string $bucket (Required) 962 | * @param array $options (Optional) 963 | * @throws OSS_Exception 964 | * @author lijie.ma@alibaba-inc.com 965 | * @since 2014-05-04 966 | * @return ResponseCore 967 | */ 968 | public function delete_bucket_website($bucket, $options = NULL){ 969 | //options 970 | $this->validate_options($options); 971 | 972 | if(!$options){ 973 | $options = array(); 974 | } 975 | 976 | //bucket 977 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 978 | 979 | $options[self::OSS_BUCKET] = $bucket; 980 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 981 | $options[self::OSS_OBJECT] = '/'; 982 | $options[self::OSS_SUB_RESOURCE] = 'website'; 983 | $response = $this->auth ($options); 984 | 985 | return $response; 986 | } 987 | 988 | /** 989 | * Set Bucket's Cors 990 | * @param string $bucket (Required) 991 | * @param array $cors_rules (Required) 992 | * @param array $options (Optional) 993 | * @throws OSS_Exception 994 | * @author lijie.ma@alibaba-inc.com 995 | * @since 2014-05-04 996 | * @return ResponseCore 997 | */ 998 | public function set_bucket_cors($bucket, $cors_rules, $options = NULL){ 999 | //options 1000 | $this->validate_options($options); 1001 | 1002 | if(!$options){ 1003 | $options = array(); 1004 | } 1005 | 1006 | //bucket 1007 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1008 | 1009 | $options[self::OSS_BUCKET] = $bucket; 1010 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1011 | $options[self::OSS_OBJECT] = '/'; 1012 | $options[self::OSS_SUB_RESOURCE] = 'cors'; 1013 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; 1014 | 1015 | $xml = new SimpleXMLElement(''); 1016 | 1017 | foreach ($cors_rules as $cors_rule){ 1018 | $cors_rule_part = $xml->addChild('CORSRule'); 1019 | 1020 | foreach ($cors_rule[self::OSS_CORS_ALLOWED_ORIGIN] as $value){ 1021 | $cors_rule_part->addChild(self::OSS_CORS_ALLOWED_ORIGIN, $value); 1022 | } 1023 | 1024 | foreach ($cors_rule[self::OSS_CORS_ALLOWED_HEADER] as $value){ 1025 | $cors_rule_part->addChild(self::OSS_CORS_ALLOWED_HEADER, $value); 1026 | } 1027 | 1028 | foreach ($cors_rule[self::OSS_CORS_ALLOWED_METHOD] as $value){ 1029 | $cors_rule_part->addChild(self::OSS_CORS_ALLOWED_METHOD, $value); 1030 | } 1031 | 1032 | foreach ($cors_rule[self::OSS_CORS_EXPOSE_HEADER] as $value){ 1033 | $cors_rule_part->addChild(self::OSS_CORS_EXPOSE_HEADER, $value); 1034 | } 1035 | 1036 | $cors_rule_part->addChild(self::OSS_CORS_MAX_AGE_SECONDS, $cors_rule[self::OSS_CORS_MAX_AGE_SECONDS]); 1037 | } 1038 | 1039 | $options[self::OSS_CONTENT] = $xml->asXML(); 1040 | return $this->auth($options); 1041 | } 1042 | /** 1043 | * Get Bucket's Cors 1044 | * @param string $bucket (Required) 1045 | * @param array $options (Optional) 1046 | * @throws OSS_Exception 1047 | * @author lijie.ma@alibaba-inc.com 1048 | * @since 2014-05-04 1049 | * @return ResponseCore 1050 | */ 1051 | public function get_bucket_cors($bucket, $options = NULL){ 1052 | //options 1053 | $this->validate_options($options); 1054 | 1055 | if(!$options){ 1056 | $options = array(); 1057 | } 1058 | 1059 | //bucket 1060 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1061 | 1062 | $options[self::OSS_BUCKET] = $bucket; 1063 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1064 | $options[self::OSS_OBJECT] = '/'; 1065 | $options[self::OSS_SUB_RESOURCE] = 'cors'; 1066 | $response = $this->auth ($options); 1067 | 1068 | return $response; 1069 | } 1070 | 1071 | /** 1072 | * Delete Bucket's Cors 1073 | * @param string $bucket (Required) 1074 | * @param array $options (Optional) 1075 | * @throws OSS_Exception 1076 | * @author lijie.ma@alibaba-inc.com 1077 | * @since 2014-05-04 1078 | * @return ResponseCore 1079 | */ 1080 | public function delete_bucket_cors($bucket, $options = NULL){ 1081 | //options 1082 | $this->validate_options($options); 1083 | 1084 | if(!$options){ 1085 | $options = array(); 1086 | } 1087 | 1088 | //bucket 1089 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1090 | 1091 | $options[self::OSS_BUCKET] = $bucket; 1092 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 1093 | $options[self::OSS_OBJECT] = '/'; 1094 | $options[self::OSS_SUB_RESOURCE] = 'cors'; 1095 | $response = $this->auth ($options); 1096 | 1097 | return $response; 1098 | } 1099 | 1100 | /** 1101 | * Options Object 1102 | * @param string $bucket (Required) 1103 | * @param array $options_request (Required) 1104 | * @param array $options (Optional) 1105 | * @throws OSS_Exception 1106 | * @author xiaobing.meng@alibaba-inc.com 1107 | * @since 2011-11-14 1108 | * @return ResponseCore 1109 | */ 1110 | public function options_object($bucket, $object, $origin, $request_method, $request_headers, $options = NULL){ 1111 | //options 1112 | $this->validate_options($options); 1113 | 1114 | if(!$options){ 1115 | $options = array(); 1116 | } 1117 | 1118 | //bucket 1119 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1120 | 1121 | $options[self::OSS_BUCKET] = $bucket; 1122 | $options[self::OSS_METHOD] = self::OSS_HTTP_OPTIONS; 1123 | $options[self::OSS_OBJECT] = $object; 1124 | $options[self::OSS_HEADERS] = array(self::OSS_OPTIONS_ORIGIN => $origin, self::OSS_OPTIONS_REQUEST_HEADERS => $request_headers, self::OSS_OPTIONS_REQUEST_METHOD => $request_method); 1125 | $response = $this->auth ( $options ); 1126 | 1127 | return $response; 1128 | } 1129 | /*%******************************************************************************************************%*/ 1130 | //Object Operation 1131 | 1132 | /** 1133 | * List Object 1134 | * @param string $bucket (Required) 1135 | * @param array $options (Optional) 1136 | * 其中options中的参数如下 1137 | * $options = array( 1138 | * 'max-keys' => max-keys用于限定此次返回object的最大数,如果不设定,默认为100,max-keys取值不能大于100。 1139 | * 'prefix' => 限定返回的object key必须以prefix作为前缀。注意使用prefix查询时,返回的key中仍会包含prefix。 1140 | * 'delimiter' => 是一个用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组元素 1141 | * 'marker' => 用户设定结果从marker之后按字母排序的第一个开始返回。 1142 | * ) 1143 | * 其中 prefix,marker用来实现分页显示效果,参数的长度必须小于256字节。 1144 | * @throws OSS_Exception 1145 | * @author xiaobing.meng@alibaba-inc.com 1146 | * @since 2011-11-14 1147 | * @return ResponseCore 1148 | */ 1149 | public function list_object($bucket, $options = NULL){ 1150 | //options 1151 | $this->validate_options($options); 1152 | 1153 | if(!$options){ 1154 | $options = array(); 1155 | } 1156 | 1157 | //bucket 1158 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1159 | 1160 | $options[self::OSS_BUCKET] = $bucket; 1161 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1162 | $options[self::OSS_OBJECT] = '/'; 1163 | $options[self::OSS_HEADERS] = array( 1164 | self::OSS_DELIMITER => isset($options[self::OSS_DELIMITER])?$options[self::OSS_DELIMITER]:'/', 1165 | self::OSS_PREFIX => isset($options[self::OSS_PREFIX])?$options[self::OSS_PREFIX]:'', 1166 | self::OSS_MAX_KEYS => isset($options[self::OSS_MAX_KEYS])?$options[self::OSS_MAX_KEYS]:self::OSS_MAX_KEYS_VALUE, 1167 | self::OSS_MARKER => isset($options[self::OSS_MARKER])?$options[self::OSS_MARKER]:'', 1168 | ); 1169 | 1170 | $response = $this->auth ( $options ); 1171 | 1172 | return $response; 1173 | 1174 | } 1175 | 1176 | /** 1177 | * 创建目录(目录和文件的区别在于,目录最后增加'/') 1178 | * @param string $bucket 1179 | * @param string $object 1180 | * @param array $options 1181 | * @author xiaobing.meng@alibaba-inc.com 1182 | * @since 2011-11-14 1183 | * @return ResponseCore 1184 | */ 1185 | public function create_object_dir($bucket, $object, $options = NULL){ 1186 | //options 1187 | $this->validate_options($options); 1188 | 1189 | if(!$options){ 1190 | $options = array(); 1191 | } 1192 | 1193 | //bucket 1194 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1195 | 1196 | //object 1197 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1198 | 1199 | $options[self::OSS_BUCKET] = $bucket; 1200 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1201 | $options[self::OSS_OBJECT] = $object.'/'; //虚拟目录需要以'/结尾' 1202 | $options[self::OSS_CONTENT_LENGTH] = array(self::OSS_CONTENT_LENGTH => 0); 1203 | 1204 | $response = $this->auth ( $options ); 1205 | 1206 | return $response; 1207 | } 1208 | 1209 | /** 1210 | * 通过在http body中添加内容来上传文件,适合比较小的文件 1211 | * 根据api约定,需要在http header中增加content-length字段 1212 | * @param string $bucket (Required) 1213 | * @param string $object (Required) 1214 | * @param string $content (Required) 1215 | * @param array $options (Optional) 1216 | * @author xiaobing.meng@alibaba-inc.com 1217 | * @since 2011-11-14 1218 | * @return ResponseCore 1219 | */ 1220 | public function upload_file_by_content($bucket, $object, $options = NULL){ 1221 | //options 1222 | $this->validate_options($options); 1223 | 1224 | if(!$options){ 1225 | $options = array(); 1226 | } 1227 | 1228 | //bucket 1229 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1230 | 1231 | //object 1232 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1233 | 1234 | //内容校验 1235 | $this->validate_content($options); 1236 | 1237 | 1238 | $objArr = explode('/', $object); 1239 | $basename = array_pop($objArr); 1240 | $extension = explode ( '.', $basename ); 1241 | $extension = array_pop ( $extension ); 1242 | $content_type = MimeTypes::get_mimetype(strtolower($extension)); 1243 | 1244 | $options[self::OSS_BUCKET] = $bucket; 1245 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1246 | $options[self::OSS_OBJECT] = $object; 1247 | 1248 | if(!isset($options[self::OSS_LENGTH])){ 1249 | $options[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]); 1250 | }else{ 1251 | $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; 1252 | } 1253 | 1254 | if(!isset($options[self::OSS_CONTENT_TYPE]) && isset($content_type) && !empty($content_type) ){ 1255 | $options[self::OSS_CONTENT_TYPE] = $content_type; 1256 | } 1257 | 1258 | $response = $this->auth ( $options ); 1259 | 1260 | return $response; 1261 | } 1262 | 1263 | /** 1264 | * 上传文件,适合比较大的文件 1265 | * @param string $bucket (Required) 1266 | * @param string $object (Required) 1267 | * @param string $file (Required) 1268 | * @param array $options (Optional) 1269 | * @author xiaobing.meng@alibaba-inc.com 1270 | * @since 2012-02-28 1271 | * @return ResponseCore 1272 | */ 1273 | public function upload_file_by_file($bucket, $object, $file, $options = NULL){ 1274 | //options 1275 | $this->validate_options($options); 1276 | 1277 | if(!$options){ 1278 | $options = array(); 1279 | } 1280 | 1281 | //bucket 1282 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1283 | 1284 | //object 1285 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1286 | 1287 | //file 1288 | $this->is_empty($file, OSS_FILE_PATH_IS_NOT_ALLOWED_EMPTY); 1289 | 1290 | if($this->chk_chinese($file)){ 1291 | $file = iconv('utf-8','gbk',$file); 1292 | } 1293 | 1294 | $options[self::OSS_FILE_UPLOAD] = $file; 1295 | 1296 | if(!file_exists($options[self::OSS_FILE_UPLOAD])){ 1297 | throw new OSS_Exception($options[self::OSS_FILE_UPLOAD].OSS_FILE_NOT_EXIST); 1298 | } 1299 | 1300 | $filesize = filesize($options[self::OSS_FILE_UPLOAD]); 1301 | $partsize = 1024 * 1024 ; //默认为 1M 1302 | 1303 | 1304 | $extension = explode ( '.', $file ); 1305 | $extension = array_pop ( $extension ); 1306 | $content_type = MimeTypes::get_mimetype(strtolower($extension)); 1307 | 1308 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1309 | $options[self::OSS_BUCKET] = $bucket; 1310 | $options[self::OSS_OBJECT] = $object; 1311 | $options[self::OSS_CONTENT_TYPE] = $content_type; 1312 | $options[self::OSS_CONTENT_LENGTH] = $filesize; 1313 | 1314 | $response = $this->auth($options); 1315 | return $response; 1316 | } 1317 | 1318 | 1319 | /** 1320 | * 拷贝Object 1321 | * @param string $bucket (Required) 1322 | * @param string $from_object (Required) 1323 | * @param string $to_object (Required) 1324 | * @param string $options (Optional) 1325 | * @author xiaobing.meng@alibaba-inc.com 1326 | * @since 2011-12-21 1327 | * @return ResponseCore 1328 | */ 1329 | public function copy_object($from_bucket, $from_object, $to_bucket, $to_object, $options = NULL){ 1330 | //options 1331 | $this->validate_options($options); 1332 | 1333 | if(!$options){ 1334 | $options = array(); 1335 | } 1336 | 1337 | //from bucket 1338 | $this->is_empty($from_bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1339 | 1340 | //to bucket 1341 | $this->is_empty($to_bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1342 | 1343 | //from object 1344 | $this->is_empty($from_object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1345 | 1346 | //to object 1347 | $this->is_empty($to_object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1348 | 1349 | $options[self::OSS_BUCKET] = $to_bucket; 1350 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1351 | $options[self::OSS_OBJECT] = $to_object; 1352 | $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_COPY_SOURCE => '/'.$from_bucket.'/'.$from_object); 1353 | 1354 | $response = $this->auth ( $options ); 1355 | 1356 | return $response; 1357 | } 1358 | 1359 | /** 1360 | * 获得object的meta信息 1361 | * @param string $bucket (Required) 1362 | * @param string $object (Required) 1363 | * @param string $options (Optional) 1364 | * @author xiaobing.meng@alibaba-inc.com 1365 | * @since 2011-11-14 1366 | * @return ResponseCore 1367 | */ 1368 | public function get_object_meta($bucket, $object, $options = NULL){ 1369 | //options 1370 | $this->validate_options($options); 1371 | 1372 | if(!$options){ 1373 | $options = array(); 1374 | } 1375 | 1376 | //bucket 1377 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1378 | 1379 | //object 1380 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1381 | 1382 | $options[self::OSS_BUCKET] = $bucket; 1383 | $options[self::OSS_METHOD] = self::OSS_HTTP_HEAD; 1384 | $options[self::OSS_OBJECT] = $object; 1385 | 1386 | $response = $this->auth ( $options ); 1387 | 1388 | return $response; 1389 | } 1390 | 1391 | /** 1392 | * 删除object 1393 | * @param string $bucket(Required) 1394 | * @param string $object (Required) 1395 | * @param array $options (Optional) 1396 | * @author xiaobing.meng@alibaba-inc.com 1397 | * @since 2011-11-14 1398 | * @return ResponseCore 1399 | */ 1400 | public function delete_object($bucket, $object, $options = NULL){ 1401 | //options 1402 | $this->validate_options($options); 1403 | 1404 | if(!$options){ 1405 | $options = array(); 1406 | } 1407 | 1408 | //bucket 1409 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1410 | 1411 | //object 1412 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1413 | 1414 | $options[self::OSS_BUCKET] = $bucket; 1415 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 1416 | $options[self::OSS_OBJECT] = $object; 1417 | 1418 | $response = $this->auth ( $options ); 1419 | 1420 | return $response; 1421 | } 1422 | 1423 | /** 1424 | * 批量删除objects 1425 | * @param string $bucket(Required) 1426 | * @param array $objects (Required) 1427 | * @param array $options (Optional) 1428 | * @author xiaobing.meng@alibaba-inc.com 1429 | * @since 2012-03-09 1430 | * @return ResponseCore 1431 | */ 1432 | public function delete_objects($bucket, $objects, $options = null){ 1433 | //options 1434 | $this->validate_options($options); 1435 | 1436 | if(!$options){ 1437 | $options = array(); 1438 | } 1439 | 1440 | //bucket 1441 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1442 | 1443 | //objects 1444 | if(!is_array($objects) || !$objects){ 1445 | throw new OSS_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.'); 1446 | } 1447 | 1448 | $options[self::OSS_METHOD] = self::OSS_HTTP_POST; 1449 | $options[self::OSS_BUCKET] = $bucket; 1450 | $options[self::OSS_OBJECT] = '/'; 1451 | $options[self::OSS_SUB_RESOURCE] = 'delete'; 1452 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; 1453 | 1454 | $xml = new SimpleXMLElement(''); 1455 | 1456 | // Quiet mode? 1457 | if (isset($options['quiet'])){ 1458 | $quiet = 'false'; 1459 | if (is_bool($options['quiet'])) { //Boolean 1460 | $quiet = $options['quiet'] ? 'true' : 'false'; 1461 | }elseif (is_string($options['quiet'])){ // String 1462 | $quiet = ($options['quiet'] === 'true') ? 'true' : 'false'; 1463 | } 1464 | 1465 | $xml->addChild('Quiet', $quiet); 1466 | } 1467 | 1468 | // Add the objects 1469 | foreach ($objects as $object){ 1470 | $xobject = $xml->addChild('Object'); 1471 | $object = $this->s_replace($object); 1472 | $xobject->addChild('Key', $object); 1473 | } 1474 | 1475 | $options[self::OSS_CONTENT] = $xml->asXML(); 1476 | 1477 | return $this->auth($options); 1478 | } 1479 | 1480 | /** 1481 | * 获得Object内容 1482 | * @param string $bucket(Required) 1483 | * @param string $object (Required) 1484 | * @param array $options (Optional) 1485 | * @author xiaobing.meng@alibaba-inc.com 1486 | * @since 2011-11-14 1487 | * @return ResponseCore 1488 | */ 1489 | public function get_object($bucket, $object, $options = NULL){ 1490 | //options 1491 | $this->validate_options($options); 1492 | 1493 | if(!$options){ 1494 | $options = array(); 1495 | } 1496 | 1497 | //bucket 1498 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1499 | 1500 | //object 1501 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1502 | 1503 | if(isset($options[self::OSS_FILE_DOWNLOAD]) && $this->chk_chinese($options[self::OSS_FILE_DOWNLOAD])){ 1504 | $options[self::OSS_FILE_DOWNLOAD] = iconv('utf-8','gbk',$options[self::OSS_FILE_DOWNLOAD]); 1505 | } 1506 | 1507 | $options[self::OSS_BUCKET] = $bucket; 1508 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1509 | $options[self::OSS_OBJECT] = $object; 1510 | 1511 | if(isset($options['lastmodified'])){ 1512 | $options[self::OSS_HEADERS][self::OSS_IF_MODIFIED_SINCE] = $options['lastmodified']; 1513 | unset($options['lastmodified']); 1514 | } 1515 | 1516 | if(isset($options['etag'])){ 1517 | $options[self::OSS_HEADERS][self::OSS_IF_NONE_MATCH] = $options['etag']; 1518 | unset($options['etag']); 1519 | } 1520 | 1521 | if(isset($options['range'])){ 1522 | $options[self::OSS_HEADERS][self::OSS_RANGE] = 'bytes=' . $options['range']; 1523 | unset($options['range']); 1524 | } 1525 | 1526 | return $this->auth ( $options ); 1527 | } 1528 | 1529 | /** 1530 | * 检测Object是否存在 1531 | * @param string $bucket(Required) 1532 | * @param string $object (Required) 1533 | * @param array $options (Optional) 1534 | * @author xiaobing.meng@alibaba-inc.com 1535 | * @since 2011-11-14 1536 | * @return boolean 1537 | */ 1538 | public function is_object_exist($bucket, $object, $options = NULL){ 1539 | //options 1540 | $this->validate_options($options); 1541 | 1542 | if(!$options){ 1543 | $options = array(); 1544 | } 1545 | 1546 | //bucket 1547 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1548 | 1549 | //object 1550 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1551 | 1552 | $options[self::OSS_BUCKET] = $bucket; 1553 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1554 | $options[self::OSS_OBJECT] = $object; 1555 | 1556 | $response = $this->get_object_meta($bucket, $object,$options); 1557 | 1558 | return $response; 1559 | } 1560 | 1561 | 1562 | /*%******************************************************************************************************%*/ 1563 | //Multi Part相关操作 1564 | 1565 | /** 1566 | * 计算文件可以分成多少个part,以及每个part的长度以及起始位置 1567 | * 方法必须在 中调用 1568 | * 1569 | * @param integer $filesize (Required) 文件大小 1570 | * @param integer $part_size (Required) part大小,默认5M 1571 | * @return array An array 包含 key-value 键值对. Key 为 `seekTo` 和 `length`. 1572 | */ 1573 | public function get_multipart_counts($filesize, $part_size = 5242880 ){ 1574 | $i = 0; 1575 | $sizecount = $filesize; 1576 | $values = array(); 1577 | 1578 | if((integer)$part_size <= 5242880){ 1579 | $part_size = 5242880; //5M 1580 | }elseif ((integer)$part_size > 524288000){ 1581 | $part_size = 524288000; //500M 1582 | }else{ 1583 | $part_size = 52428800; //50M 1584 | } 1585 | 1586 | while ($sizecount > 0) 1587 | { 1588 | $sizecount -= $part_size; 1589 | $values[] = array( 1590 | self::OSS_SEEK_TO => ($part_size * $i), 1591 | self::OSS_LENGTH => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)), 1592 | ); 1593 | $i++; 1594 | } 1595 | 1596 | return $values; 1597 | } 1598 | 1599 | /** 1600 | * 初始化multi-part upload,并且返回uploadId 1601 | * @param string $bucket (Required) Bucket名称 1602 | * @param string $object (Required) Object名称 1603 | * @param array $options (Optional) Key-Value数组,其中可以包括以下的key 1604 | * @return ResponseCore 1605 | */ 1606 | public function initiate_multipart_upload($bucket, $object, $options = NULL){ 1607 | //options 1608 | $this->validate_options($options); 1609 | 1610 | if(!$options){ 1611 | $options = array(); 1612 | } 1613 | 1614 | //bucket 1615 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1616 | 1617 | //object 1618 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1619 | 1620 | // 发送请求 1621 | $options[self::OSS_METHOD] = self::OSS_HTTP_POST; 1622 | $options[self::OSS_BUCKET] = $bucket; 1623 | $options[self::OSS_OBJECT] = $object; 1624 | $options[self::OSS_SUB_RESOURCE] = 'uploads'; 1625 | $options[self::OSS_CONTENT] = ''; 1626 | //$options[self::OSS_CONTENT_LENGTH] = 0; 1627 | $options[self::OSS_HEADERS] = array(self::OSS_CONTENT_TYPE => 'application/octet-stream'); 1628 | 1629 | $response = $this->auth ( $options ); 1630 | 1631 | return $response; 1632 | } 1633 | 1634 | /** 1635 | * 上传part 1636 | * @param string $bucket (Required) Bucket名称 1637 | * @param string $object (Required) Object名称 1638 | * @param string $upload_id (Required) uploadId 1639 | * @param array $options (Optional) Key-Value数组,其中可以包括以下的key 1640 | * @return ResponseCore 1641 | */ 1642 | public function upload_part($bucket, $object, $upload_id, $options = null){ 1643 | //options 1644 | $this->validate_options($options); 1645 | 1646 | if(!$options){ 1647 | $options = array(); 1648 | } 1649 | 1650 | //bucket 1651 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1652 | 1653 | //object 1654 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1655 | 1656 | if (!isset($options[self::OSS_FILE_UPLOAD]) || !isset($options['partNumber'])){ 1657 | throw new OSS_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().'); 1658 | } 1659 | 1660 | $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; 1661 | $options[self::OSS_BUCKET] = $bucket; 1662 | $options[self::OSS_OBJECT] = $object; 1663 | $options[self::OSS_UPLOAD_ID] = $upload_id; 1664 | 1665 | if(isset($options[self::OSS_LENGTH])){ 1666 | $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; 1667 | } 1668 | 1669 | return $this->auth($options); 1670 | } 1671 | 1672 | /** 1673 | * list part 1674 | * @param string $bucket (Required) Bucket名称 1675 | * @param string $object (Required) Object名称 1676 | * @param string $upload_id (Required) uploadId 1677 | * @param array $options (Optional) Key-Value数组,其中可以包括以下的key 1678 | * @return ResponseCore 1679 | */ 1680 | public function list_parts($bucket, $object, $upload_id, $options = null){ 1681 | //options 1682 | $this->validate_options($options); 1683 | 1684 | if(!$options){ 1685 | $options = array(); 1686 | } 1687 | 1688 | //bucket 1689 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1690 | 1691 | //object 1692 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1693 | 1694 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1695 | $options[self::OSS_BUCKET] = $bucket; 1696 | $options[self::OSS_OBJECT] = $object; 1697 | $options[self::OSS_UPLOAD_ID] = $upload_id; 1698 | $options[self::OSS_QUERY_STRING] = array(); 1699 | 1700 | foreach (array('max-parts', 'part-number-marker') as $param){ 1701 | if (isset($options[$param])){ 1702 | $options[self::OSS_QUERY_STRING][$param] = $options[$param]; 1703 | unset($options[$param]); 1704 | } 1705 | } 1706 | 1707 | return $this->auth($options); 1708 | } 1709 | 1710 | /** 1711 | * 中止上传mulit-part upload 1712 | * @param string $bucket (Required) Bucket名称 1713 | * @param string $object (Required) Object名称 1714 | * @param string $upload_id (Required) uploadId 1715 | * @param array $options (Optional) Key-Value数组,其中可以包括以下的key 1716 | * @return ResponseCore 1717 | */ 1718 | public function abort_multipart_upload($bucket, $object, $upload_id, $options = NULL){ 1719 | //options 1720 | $this->validate_options($options); 1721 | 1722 | if(!$options){ 1723 | $options = array(); 1724 | } 1725 | 1726 | //bucket 1727 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1728 | 1729 | //object 1730 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1731 | 1732 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 1733 | $options[self::OSS_BUCKET] = $bucket; 1734 | $options[self::OSS_OBJECT] = $object; 1735 | $options[self::OSS_UPLOAD_ID] = $upload_id; 1736 | 1737 | return $this->auth($options); 1738 | } 1739 | 1740 | /** 1741 | * 完成multi-part上传 1742 | * @param string $bucket (Required) Bucket名称 1743 | * @param string $object (Required) Object名称 1744 | * @param string $upload_id (Required) uploadId 1745 | * @param string $parts xml格式文件 1746 | * @param array $options (Optional) Key-Value数组,其中可以包括以下的key 1747 | * @return ResponseCore 1748 | */ 1749 | public function complete_multipart_upload($bucket, $object, $upload_id, $parts, $options = NULL){ 1750 | //options 1751 | $this->validate_options($options); 1752 | 1753 | if(!$options){ 1754 | $options = array(); 1755 | } 1756 | 1757 | //bucket 1758 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1759 | 1760 | //object 1761 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1762 | 1763 | $options[self::OSS_METHOD] = self::OSS_HTTP_POST; 1764 | $options[self::OSS_BUCKET] = $bucket; 1765 | $options[self::OSS_OBJECT] = $object; 1766 | $options[self::OSS_UPLOAD_ID] = $upload_id; 1767 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; 1768 | 1769 | 1770 | if(is_string($parts)){ 1771 | $options[self::OSS_CONTENT] = $parts; 1772 | }else if($parts instanceof SimpleXMLElement){ 1773 | $options[self::OSS_CONTENT] = $parts->asXML(); 1774 | }else if((is_array($parts) || $parts instanceof ResponseCore)){ 1775 | $xml = new SimpleXMLElement(''); 1776 | 1777 | if (is_array($parts)){ 1778 | //生成关联的xml 1779 | foreach ($parts as $node){ 1780 | $part = $xml->addChild('Part'); 1781 | $part->addChild('PartNumber', $node['PartNumber']); 1782 | $part->addChild('ETag', $node['ETag']); 1783 | } 1784 | }elseif ($parts instanceof ResponseCore){ 1785 | foreach ($parts->body->Part as $node){ 1786 | $part = $xml->addChild('Part'); 1787 | $part->addChild('PartNumber', (string) $node->PartNumber); 1788 | $part->addChild('ETag', (string) $node->ETag); 1789 | } 1790 | } 1791 | 1792 | $options[self::OSS_CONTENT] = $xml->asXML(); 1793 | } 1794 | 1795 | return $this->auth($options); 1796 | } 1797 | 1798 | /** 1799 | * 列出multipart上传 1800 | * @param string $bucket (Requeired) bucket 1801 | * @param array $options (Optional) 关联数组 1802 | * @author xiaobing.meng@alibaba-inc.com 1803 | * @since 2012-03-05 1804 | * @return ResponseCore 1805 | */ 1806 | public function list_multipart_uploads($bucket, $options = null){ 1807 | //options 1808 | $this->validate_options($options); 1809 | 1810 | if(!$options){ 1811 | $options = array(); 1812 | } 1813 | 1814 | //bucket 1815 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1816 | 1817 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 1818 | $options[self::OSS_BUCKET] = $bucket; 1819 | $options[self::OSS_OBJECT] = '/'; 1820 | $options[self::OSS_SUB_RESOURCE] = 'uploads'; 1821 | 1822 | foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param){ 1823 | if (isset($options[$param])){ 1824 | $options[self::OSS_QUERY_STRING][$param] = $options[$param]; 1825 | unset($options[$param]); 1826 | } 1827 | } 1828 | 1829 | return $this->auth($options); 1830 | } 1831 | 1832 | /** 1833 | * multipart上传统一封装,从初始化到完成multipart,以及出错后中止动作 1834 | * @param unknown_type $bucket 1835 | * @param unknown_type $object 1836 | * @param unknown_type $options 1837 | * @author xiaobing.meng@alibaba-inc.com 1838 | * @since 2012-03-05 1839 | * @return ResponseCore 1840 | */ 1841 | public function create_mpu_object($bucket, $object, $options = null){ 1842 | //options 1843 | $this->validate_options($options); 1844 | 1845 | if(!$options){ 1846 | $options = array(); 1847 | } 1848 | 1849 | //bucket 1850 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1851 | 1852 | //object 1853 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 1854 | 1855 | if(isset($options[self::OSS_LENGTH])){ 1856 | $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; 1857 | unset($options[self::OSS_LENGTH]); 1858 | } 1859 | 1860 | if(isset($options[self::OSS_FILE_UPLOAD])){ 1861 | if($this->chk_chinese($options[self::OSS_FILE_UPLOAD])){ 1862 | $options[self::OSS_FILE_UPLOAD] = mb_convert_encoding($options[self::OSS_FILE_UPLOAD],'UTF-8'); 1863 | } 1864 | } 1865 | 1866 | if(!isset($options[self::OSS_FILE_UPLOAD])){ 1867 | throw new OSS_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().'); 1868 | }elseif (is_resource($options[self::OSS_FILE_UPLOAD])){ 1869 | $upload_position = isset($options[self::OSS_SEEK_TO]) ? (integer) $options[self::OSS_SEEK_TO] : ftell($options[self::OSS_FILE_UPLOAD]); 1870 | $upload_filesize = isset($options[self::OSS_CONTENT_LENGTH]) ? (integer) $options[self::OSS_CONTENT_LENGTH] : null; 1871 | 1872 | if (!isset($upload_filesize) && $upload_position !== false){ 1873 | $stats = fstat($options[self::OSS_FILE_UPLOAD]); 1874 | 1875 | if ($stats && $stats[self::OSS_SIZE] >= 0){ 1876 | $upload_filesize = $stats[self::OSS_SIZE] - $upload_position; 1877 | } 1878 | } 1879 | }else{ 1880 | $upload_position = isset($options[self::OSS_SEEK_TO]) ? (integer) $options[self::OSS_SEEK_TO] : 0; 1881 | 1882 | if (isset($options[self::OSS_CONTENT_TYPE])){ 1883 | $upload_filesize = (integer) $options[self::OSS_CONTENT_TYPE]; 1884 | } 1885 | else{ 1886 | $upload_filesize = filesize($options[self::OSS_FILE_UPLOAD]); 1887 | 1888 | if ($upload_filesize !== false){ 1889 | $upload_filesize -= $upload_position; 1890 | } 1891 | } 1892 | } 1893 | 1894 | if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0){ 1895 | throw new OSS_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().'); 1896 | } 1897 | 1898 | // 处理partSize 1899 | if (isset($options[self::OSS_PART_SIZE])){ 1900 | // 小于5M 1901 | if ((integer) $options[self::OSS_PART_SIZE] <= 5242880){ 1902 | $options[self::OSS_PART_SIZE] = 5242880; // 5 MB 1903 | } 1904 | // 大于500M 1905 | elseif ((integer) $options[self::OSS_PART_SIZE] > 524288000){ 1906 | $options[self::OSS_PART_SIZE] = 524288000; // 500 MB 1907 | } 1908 | } 1909 | else{ 1910 | $options[self::OSS_PART_SIZE] = 52428800; // 50 MB 1911 | } 1912 | 1913 | // 如果上传的文件小于partSize,则直接使用普通方式上传 1914 | if ($upload_filesize < $options[self::OSS_PART_SIZE] && !isset($options['uploadId'])){ 1915 | return $this->upload_file_by_file($bucket, $object, $options[self::OSS_FILE_UPLOAD]); 1916 | } 1917 | 1918 | // 初始化multipart 1919 | if (isset($options['uploadId'])){ 1920 | $upload_id = $options['uploadId']; 1921 | }else{ 1922 | //初始化 1923 | $upload = $this->initiate_multipart_upload($bucket, $object); 1924 | 1925 | if (!$upload->isOK()){ 1926 | throw new OSS_Exception('Init multi-part upload failed...'); 1927 | } 1928 | $xml = new SimpleXmlIterator($upload->body); 1929 | $uploadId = (string)$xml->UploadId; 1930 | } 1931 | 1932 | // 或的分片 1933 | $pieces = $this->get_multipart_counts($upload_filesize, (integer) $options[self::OSS_PART_SIZE]); 1934 | 1935 | $response_upload_part = array(); 1936 | foreach ($pieces as $i => $piece){ 1937 | $response_upload_part[] = $this->upload_part($bucket, $object, $uploadId, array( 1938 | //'expect' => '100-continue', 1939 | self::OSS_FILE_UPLOAD => $options[self::OSS_FILE_UPLOAD], 1940 | 'partNumber' => ($i + 1), 1941 | self::OSS_SEEK_TO => $upload_position + (integer) $piece[self::OSS_SEEK_TO], 1942 | self::OSS_LENGTH => (integer) $piece[self::OSS_LENGTH], 1943 | )); 1944 | } 1945 | 1946 | $upload_parts = array(); 1947 | $upload_part_result = true; 1948 | 1949 | foreach ($response_upload_part as $i=>$response){ 1950 | $upload_part_result = $upload_part_result && $response->isOk(); 1951 | } 1952 | 1953 | if(!$upload_part_result){ 1954 | throw new OSS_Exception('any part upload failed...,pls try again'); 1955 | } 1956 | 1957 | foreach ($response_upload_part as $i=>$response){ 1958 | $upload_parts[] = array( 1959 | 'PartNumber' => ($i + 1), 1960 | 'ETag' => (string) $response->header['etag'] 1961 | ); 1962 | } 1963 | 1964 | return $this->complete_multipart_upload($bucket, $object, $uploadId, $upload_parts); 1965 | } 1966 | 1967 | 1968 | /** 1969 | * 通过Multi-Part方式上传整个目录,其中的object默认为文件名 1970 | * @param string $bucket (Required) 1971 | * @param string $dir (Required) 必选 1972 | * @param boolean $recursive (Optional) 是否递归,如果为true,则递归读取所有目录,默认为不递归读取 1973 | * @param string $exclude 需要过滤的文件 1974 | * @param array $options (Optional) 关联数组 1975 | * @author xiaobing.meng@alibaba-inc.com 1976 | * @since 2012-03-05 1977 | * @return ResponseCore 1978 | */ 1979 | public function create_mtu_object_by_dir($bucket, $dir, $recursive = false, $exclude = ".|..|.svn", $options = null){ 1980 | //options 1981 | $this->validate_options($options); 1982 | 1983 | //bucket 1984 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 1985 | 1986 | if($this->chk_chinese($dir)){ 1987 | $dir = iconv('utf-8','gbk',$dir); 1988 | } 1989 | 1990 | //判断是否目录 1991 | if(!is_dir($dir)){ 1992 | throw new OSS_Exception($dir.' is not a directory...,pls check it'); 1993 | } 1994 | 1995 | $file_list_array = $this->read_dir($dir,$exclude,$recursive); 1996 | 1997 | if(!$file_list_array){ 1998 | throw new OSS_Exception($dir.' is empty...'); 1999 | } 2000 | 2001 | $index = 1; 2002 | 2003 | foreach ($file_list_array as $item){ 2004 | $options = array( 2005 | self::OSS_FILE_UPLOAD => $item['path'], 2006 | self::OSS_PART_SIZE => 5242880, 2007 | ); 2008 | 2009 | echo $index++.". "; 2010 | $response = $this->create_mpu_object($bucket, $item['file'],$options); 2011 | if($response->isOK()){ 2012 | echo "Upload file {".$item['path']." } successful..\n"; 2013 | }else{ 2014 | echo "Upload file {".$item['path']." } failed..\n"; 2015 | continue; 2016 | } 2017 | } 2018 | } 2019 | 2020 | /** 2021 | * 通过multi-part方式上传目录(优化版) 2022 | * $options = array( 2023 | * 'bucket' => (Required) 2024 | * 'object' => (Optional) 2025 | * 'directory' => (Required) 2026 | * 'exclude' => (Optional) 2027 | * 'recursive' => (Optional) 2028 | * ) 2029 | */ 2030 | public function batch_upload_file($options = NULL){ 2031 | if((NULL == $options) || !isset($options['bucket']) || empty($options['bucket']) || !isset($options['directory']) ||empty($options['directory']) ) { 2032 | throw new OSS_Exception('Bad Request',400); 2033 | } 2034 | 2035 | $bucket = $options['bucket']; unset($options['bucket']); 2036 | 2037 | $directory = $options['directory']; unset($options['directory']); 2038 | if($this->chk_chinese($directory)){ 2039 | $directory = iconv('utf-8','gbk',$directory); 2040 | } 2041 | 2042 | //判断是否目录 2043 | if(!is_dir($directory)){ 2044 | throw new OSS_Exception($dir.' is not a directory...,pls check it'); 2045 | } 2046 | 2047 | $object = ''; 2048 | if(isset($options['object'])){ 2049 | $object = $options['object']; 2050 | unset($options['object']); 2051 | } 2052 | 2053 | $exclude = '.|..|.svn'; 2054 | if (isset($options['exclude']) && !empty($options['exclude'])){ 2055 | $exclude = $options['exclude']; 2056 | unset($options['exclude']); 2057 | } 2058 | 2059 | $recursive = false; 2060 | if(isset($options['recursive']) && !empty($options['recursive'])){ 2061 | if(in_array($options['recursive'],array(true,false))){ 2062 | $recursive = $options['recursive']; 2063 | } 2064 | unset($options['recursive']); 2065 | } 2066 | 2067 | //read directory 2068 | $file_list_array = $this->read_dir($directory,$exclude,$recursive); 2069 | 2070 | if(!$file_list_array){ 2071 | throw new OSS_Exception($directory.' is empty...'); 2072 | } 2073 | 2074 | $index = 1; 2075 | 2076 | foreach ($file_list_array as $item){ 2077 | $options = array( 2078 | self::OSS_FILE_UPLOAD => $item['path'], 2079 | self::OSS_PART_SIZE => 5242880, 2080 | ); 2081 | 2082 | echo $index++.". "; 2083 | $response = $this->create_mpu_object($bucket, (!empty($object)?$object.'/':'').$item['file'],$options); 2084 | if($response->isOK()){ 2085 | echo "Upload file {".$item['path']." } successful..\n"; 2086 | }else{ 2087 | echo "Upload file {".$item['path']." } failed..\n"; 2088 | continue; 2089 | } 2090 | } 2091 | } 2092 | 2093 | 2094 | /*%******************************************************************************************************%*/ 2095 | //Object Group相关操作 2096 | 2097 | /** 2098 | * 创建Object Group 2099 | * @param string $object_group (Required) Object Group名称 2100 | * @param string $bucket (Required) Bucket名称 2101 | * @param array $object_arry (Required) object数组,所有的object必须在同一个bucket下 2102 | * 其中$object 数组的格式如下: 2103 | * $object = array( 2104 | * $object1, 2105 | * $object2, 2106 | * ... 2107 | * ) 2108 | * @param array $options (Optional) 2109 | * @author xiaobing.meng@alibaba-inc.com 2110 | * @since 2011-11-14 2111 | * @return ResponseCore 2112 | */ 2113 | public function create_object_group($bucket, $object_group, $object_arry, $options = NULL){ 2114 | //options 2115 | $this->validate_options($options); 2116 | 2117 | //bucket 2118 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2119 | 2120 | //object group 2121 | $this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY); 2122 | 2123 | $options[self::OSS_BUCKET] = $bucket; 2124 | $options[self::OSS_METHOD] = self::OSS_HTTP_POST; 2125 | $options[self::OSS_OBJECT] = $object_group; 2126 | $options[self::OSS_CONTENT_TYPE] = 'txt/xml'; //重设Content-Type 2127 | $options[self::OSS_SUB_RESOURCE] = 'group'; //设置?group 2128 | $options[self::OSS_CONTENT] = $this->make_object_group_xml($bucket,$object_arry); //格式化xml 2129 | 2130 | $response = $this->auth ( $options ); 2131 | 2132 | return $response; 2133 | } 2134 | 2135 | /** 2136 | * 获取Object Group 2137 | * @param string $object_group (Required) 2138 | * @param string $bucket (Required) 2139 | * @param array $options (Optional) 2140 | * @author xiaobing.meng@alibaba-inc.com 2141 | * @since 2011-11-14 2142 | * @return ResponseCore 2143 | */ 2144 | public function get_object_group($bucket, $object_group, $options = NULL){ 2145 | //options 2146 | $this->validate_options($options); 2147 | 2148 | //bucket 2149 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2150 | 2151 | //object group 2152 | $this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY); 2153 | 2154 | $options[self::OSS_BUCKET] = $bucket; 2155 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 2156 | $options[self::OSS_OBJECT] = $object_group; 2157 | //$options[self::OSS_OBJECT_GROUP] = true; //设置?group 2158 | //$options[self::OSS_CONTENT_TYPE] = 'txt/xml'; //重设Content-Type 2159 | $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP); //header中的x-oss-file-group不能为空,否则返回值错误 2160 | 2161 | $response = $this->auth ( $options ); 2162 | 2163 | return $response; 2164 | } 2165 | 2166 | /** 2167 | * 获取Object Group 的Object List信息 2168 | * @param string $object_group (Required) 2169 | * @param string $bucket (Required) 2170 | * @param array $options (Optional) 2171 | * @author xiaobing.meng@alibaba-inc.com 2172 | * @since 2011-11-14 2173 | * @return ResponseCore 2174 | */ 2175 | public function get_object_group_index($bucket, $object_group, $options = NULL){ 2176 | //options 2177 | $this->validate_options($options); 2178 | 2179 | //bucket 2180 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2181 | 2182 | //object group 2183 | $this->is_empty($object_group,OSS_OBJECT_GROUP_IS_NOT_ALLOWED_EMPTY); 2184 | 2185 | $options[self::OSS_BUCKET] = $bucket; 2186 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 2187 | $options[self::OSS_OBJECT] = $object_group; 2188 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; //重设Content-Type 2189 | //$options[self::OSS_OBJECT_GROUP] = true; //设置?group 2190 | $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP); 2191 | 2192 | $response = $this->auth ( $options ); 2193 | 2194 | return $response; 2195 | } 2196 | 2197 | /** 2198 | * 获得object group的meta信息 2199 | * @param string $bucket (Required) 2200 | * @param string $object_group (Required) 2201 | * @param string $options (Optional) 2202 | * @author xiaobing.meng@alibaba-inc.com 2203 | * @since 2011-11-14 2204 | * @return ResponseCore 2205 | */ 2206 | public function get_object_group_meta($bucket, $object_group, $options = NULL){ 2207 | //options 2208 | $this->validate_options($options); 2209 | 2210 | if(!$options){ 2211 | $options = array(); 2212 | } 2213 | 2214 | //bucket 2215 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2216 | 2217 | //object group 2218 | $this->is_empty($object_group,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 2219 | 2220 | $options[self::OSS_BUCKET] = $bucket; 2221 | $options[self::OSS_METHOD] = self::OSS_HTTP_HEAD; 2222 | $options[self::OSS_OBJECT] = $object_group; 2223 | $options[self::OSS_CONTENT_TYPE] = 'application/xml'; //重设Content-Type 2224 | //$options[self::OSS_SUB_RESOURCE] = 'group'; //设置?group 2225 | $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_GROUP => self::OSS_OBJECT_GROUP); 2226 | 2227 | $response = $this->auth ( $options ); 2228 | 2229 | return $response; 2230 | } 2231 | 2232 | /** 2233 | * 删除Object Group 2234 | * @param string $bucket(Required) 2235 | * @param string $object_group (Required) 2236 | * @param array $options (Optional) 2237 | * @author xiaobing.meng@alibaba-inc.com 2238 | * @since 2011-11-14 2239 | * @return ResponseCore 2240 | */ 2241 | public function delete_object_group($bucket, $object_group, $options = NULL){ 2242 | //options 2243 | $this->validate_options($options); 2244 | 2245 | if(!$options){ 2246 | $options = array(); 2247 | } 2248 | 2249 | //bucket 2250 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2251 | 2252 | //object group 2253 | $this->is_empty($object_group,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 2254 | 2255 | $options[self::OSS_BUCKET] = $bucket; 2256 | $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; 2257 | $options[self::OSS_OBJECT] = $object_group; 2258 | 2259 | $response = $this->auth ( $options ); 2260 | 2261 | return $response; 2262 | } 2263 | 2264 | 2265 | /*%******************************************************************************************************%*/ 2266 | //带签名的url相关 2267 | 2268 | /** 2269 | * 获取带签名的url 2270 | * @param string $bucket (Required) 2271 | * @param string $object (Required) 2272 | * @param int $timeout (Optional) 2273 | * @param array $options (Optional) 2274 | * @author xiaobing.meng@alibaba-inc.com 2275 | * @since 2011-12-21 2276 | * @return string 2277 | */ 2278 | public function get_sign_url($bucket, $object, $timeout = 60, $options = NULL){ 2279 | //options 2280 | $this->validate_options($options); 2281 | 2282 | if(!$options){ 2283 | $options = array(); 2284 | } 2285 | 2286 | //bucket 2287 | $this->is_empty($bucket,OSS_BUCKET_IS_NOT_ALLOWED_EMPTY); 2288 | 2289 | //object 2290 | $this->is_empty($object,OSS_OBJECT_IS_NOT_ALLOWED_EMPTY); 2291 | 2292 | $options[self::OSS_BUCKET] = $bucket; 2293 | $options[self::OSS_OBJECT] = $object; 2294 | $options[self::OSS_METHOD] = self::OSS_HTTP_GET; 2295 | $options[self::OSS_CONTENT_TYPE] = ''; 2296 | 2297 | $timeout = time() + $timeout; 2298 | $options[self::OSS_PREAUTH] = $timeout; 2299 | $options[self::OSS_DATE] = $timeout; 2300 | 2301 | return $this->auth($options); 2302 | } 2303 | 2304 | /*%******************************************************************************************************%*/ 2305 | //日志相关 2306 | 2307 | /** 2308 | * 记录日志 2309 | * @param string $msg (Required) 2310 | * @throws OSS_Exception 2311 | * @author xiaobing.meng@alibaba-inc.com 2312 | * @since 2011-12-27 2313 | * @return void 2314 | */ 2315 | private function log($msg){ 2316 | if(defined('ALI_LOG_PATH') ){ 2317 | $log_path = ALI_LOG_PATH; 2318 | if(empty($log_path) || !file_exists($log_path)){ 2319 | throw new OSS_Exception($log_path.OSS_LOG_PATH_NOT_EXIST); 2320 | } 2321 | }else{ 2322 | $log_path = dirname(__FILE__).DIRECTORY_SEPARATOR.'logs'.DIRECTORY_SEPARATOR; 2323 | } 2324 | 2325 | //检测日志目录是否存在 2326 | if(!file_exists($log_path)){ 2327 | throw new OSS_Exception(OSS_LOG_PATH_NOT_EXIST); 2328 | } 2329 | 2330 | $log_name = $log_path.'oss_sdk_php_'.date('Y-m-d').'.log'; 2331 | 2332 | if(ALI_DISPLAY_LOG){ 2333 | echo $msg."\n
"; 2334 | } 2335 | 2336 | if(ALI_LOG){ 2337 | if(!error_log(date('Y-m-d H:i:s')." : ".$msg."\n", 3,$log_name)){ 2338 | throw new OSS_Exception(OSS_WRITE_LOG_TO_FILE_FAILED); 2339 | } 2340 | } 2341 | } 2342 | 2343 | 2344 | /*%******************************************************************************************************%*/ 2345 | //工具类相关 2346 | 2347 | /** 2348 | * 生成query params 2349 | * @param array $array 关联数组 2350 | * @author xiaobing.meng@alibaba-inc.com 2351 | * @since 2012-03-04 2352 | * @return string 返回诸如 key1=value1&key2=value2 2353 | */ 2354 | public function to_query_string($options = array()){ 2355 | $temp = array(); 2356 | 2357 | foreach ($options as $key => $value){ 2358 | if (is_string($key) && !is_array($value)){ 2359 | $temp[] = rawurlencode($key) . '=' . rawurlencode($value); 2360 | } 2361 | } 2362 | 2363 | return implode('&', $temp); 2364 | } 2365 | 2366 | /** 2367 | * 转化十六进制的数据为base64 2368 | * 2369 | * @param string $str (Required) 要转化的字符串 2370 | * @author xiaobing.meng@alibaba-inc.com 2371 | * @since 2012-03-20 2372 | * @return string Base64-encoded string. 2373 | */ 2374 | private function hex_to_base64($str){ 2375 | $result = ''; 2376 | 2377 | for ($i = 0; $i < strlen($str); $i += 2){ 2378 | $result .= chr(hexdec(substr($str, $i, 2))); 2379 | } 2380 | 2381 | return base64_encode($result); 2382 | } 2383 | 2384 | private function s_replace($subject){ 2385 | $search = array('<','>','&','\'','"'); 2386 | $replace = array('<','>','&',''','"'); 2387 | return str_replace($search, $replace, $subject); 2388 | } 2389 | 2390 | /** 2391 | * 替换控制字符,诸如 替换为%1A 2392 | * @param unknown $invalid_xml_chars 2393 | */ 2394 | public function replace_invalid_xml_char($subject){ 2395 | $search = array( 2396 | '','','','','','','','',' ',' ',' ',' ',' ', 2397 | '','','','','','','','','','','','','', 2398 | '','','','','','' 2399 | ); 2400 | $replace = array( 2401 | '%01','%02','%03','%04','%05','%06','%07','%08','%09','%0A','%0B','%0C','%0D', 2402 | '%0E','%0F','%10','%11','%12','%13','%14','%15','%16','%17','%18','%19','%1A', 2403 | '%1B','%1C','%1D','%1E','%1F','%7F' 2404 | ); 2405 | 2406 | return str_replace($search, $replace, $subject); 2407 | } 2408 | 2409 | /** 2410 | * 检测是否含有中文 2411 | * @param string $subject 2412 | * @author xiaobing.meng@alibaba-inc.com 2413 | * @since 2012-06-06 2414 | * @return boolean 2415 | */ 2416 | private function chk_chinese($str){ 2417 | return preg_match('/[\x80-\xff]./', $str); 2418 | } 2419 | 2420 | /** 2421 | * 检测是否GB2312编码 2422 | * @param string $str 2423 | * @author xiaobing.meng@alibaba-inc.com 2424 | * @since 2012-03-20 2425 | * @return boolean false UTF-8编码 TRUE GB2312编码 2426 | */ 2427 | function is_gb2312($str) { 2428 | for($i=0; $i 127) { 2431 | if( ($v >= 228) && ($v <= 233) ){ 2432 | if( ($i+2) >= (strlen($str) - 1)) return true; // not enough characters 2433 | $v1 = ord( $str[$i+1] ); 2434 | $v2 = ord( $str[$i+2] ); 2435 | if( ($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191) ) 2436 | return false; //UTF-8编码 2437 | else 2438 | return true; //GB编码 2439 | } 2440 | } 2441 | } 2442 | } 2443 | 2444 | 2445 | /** 2446 | * 检测是否GBK编码 2447 | * @param string $str 2448 | * @param boolean $gbk 2449 | * @author xiaobing.meng@alibaba-inc.com 2450 | * @since 2012-06-04 2451 | * @return boolean 2452 | */ 2453 | private function check_char($str, $gbk = true){ 2454 | for($i=0; $i 127){ 2457 | if( ($v >= 228) && ($v <= 233) ){ 2458 | if(($i+2)>= (strlen($str)-1)) return $gbk?true:FALSE; // not enough characters 2459 | $v1 = ord( $str[$i+1] ); $v2 = ord( $str[$i+2] ); 2460 | if($gbk){ 2461 | return (($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191))?FALSE:TRUE;//GBK 2462 | }else{ 2463 | return (($v1 >= 128) && ($v1 <=191) && ($v2 >=128) && ($v2 <= 191))?TRUE:FALSE; 2464 | } 2465 | } 2466 | } 2467 | } 2468 | return $gbk?TRUE:FALSE; 2469 | } 2470 | 2471 | 2472 | /** 2473 | * 读取目录 2474 | * @param string $dir (Required) 目录名 2475 | * @param boolean $recursive (Optional) 是否递归,默认为false 2476 | * @author xiaobing.meng@alibaba-inc.com 2477 | * @since 2012-03-05 2478 | * @return array 2479 | */ 2480 | private function read_dir($dir, $exclude = ".|..|.svn", $recursive = false){ 2481 | static $file_list_array = array(); 2482 | 2483 | $exclude_array = explode("|", $exclude); 2484 | //读取目录 2485 | if($handle = opendir($dir)){ 2486 | while ( false !== ($file = readdir($handle))){ 2487 | if(!in_array(strtolower($file),$exclude_array)){ 2488 | $new_file = $dir.'/'.$file; 2489 | if(is_dir($new_file) && $recursive){ 2490 | $this->read_dir($new_file,$exclude,$recursive); 2491 | }else{ 2492 | $file_list_array[] = array( 2493 | 'path' => $new_file, 2494 | 'file' => $file, 2495 | ); 2496 | } 2497 | } 2498 | } 2499 | 2500 | closedir($handle); 2501 | } 2502 | 2503 | return $file_list_array; 2504 | } 2505 | 2506 | 2507 | /** 2508 | * 转化object数组为固定个xml格式 2509 | * @param string $bucket (Required) 2510 | * @param array $object_array (Required) 2511 | * @throws OSS_Exception 2512 | * @author xiaobing.meng@alibaba-inc.com 2513 | * @since 2011-12-27 2514 | * @return string 2515 | */ 2516 | private function make_object_group_xml($bucket, $object_array){ 2517 | $xml = ''; 2518 | $xml .= ''; 2519 | 2520 | if($object_array){ 2521 | if(count($object_array) > self::OSS_MAX_OBJECT_GROUP_VALUE){ 2522 | throw new OSS_Exception(OSS_OBJECT_GROUP_TOO_MANY_OBJECT, '-401'); 2523 | } 2524 | $index = 1; 2525 | foreach ($object_array as $key=>$value){ 2526 | $object_meta = (array)$this->get_object_meta($bucket, $value); 2527 | if(isset($object_meta) && isset($object_meta['status']) && isset($object_meta['header']) && isset($object_meta['header']['etag']) && $object_meta['status'] == 200){ 2528 | $xml .= ''; 2529 | $xml .= ''.intval($index).''; 2530 | $xml .= ''.$value.''; 2531 | $xml .= ''.$object_meta['header']['etag'].''; 2532 | $xml .= ''; 2533 | 2534 | $index++; 2535 | } 2536 | } 2537 | }else{ 2538 | throw new OSS_Exception(OSS_OBJECT_ARRAY_IS_EMPTY, '-400'); 2539 | } 2540 | 2541 | $xml .= ''; 2542 | 2543 | return $xml; 2544 | } 2545 | 2546 | /** 2547 | * 检验bucket名称是否合法 2548 | * bucket的命名规范: 2549 | * 1. 只能包括小写字母,数字 2550 | * 2. 必须以小写字母或者数字开头 2551 | * 3. 长度必须在3-63字节之间 2552 | * @param string $bucket (Required) 2553 | * @author xiaobing.meng@alibaba-inc.com 2554 | * @since 2011-12-27 2555 | * @return boolean 2556 | */ 2557 | private function validate_bucket($bucket){ 2558 | $pattern = '/^[a-z0-9][a-z0-9-]{2,62}$/'; 2559 | if (! preg_match ( $pattern, $bucket )) { 2560 | return false; 2561 | } 2562 | return true; 2563 | } 2564 | 2565 | /** 2566 | * 检验object名称是否合法 2567 | * object命名规范: 2568 | * 1. 规则长度必须在1-1023字节之间 2569 | * 2. 使用UTF-8编码 2570 | * @param string $object (Required) 2571 | * @author xiaobing.meng@alibaba-inc.com 2572 | * @since 2011-12-27 2573 | * @return boolean 2574 | */ 2575 | private function validate_object($object){ 2576 | $pattern = '/^.{1,1023}$/'; 2577 | if (empty ( $object ) || ! preg_match ( $pattern, $object )) { 2578 | return false; 2579 | } 2580 | return true; 2581 | } 2582 | 2583 | /** 2584 | * 检验$options 2585 | * @param array $options (Optional) 2586 | * @throws OSS_Exception 2587 | * @author xiaobing.meng@alibaba-inc.com 2588 | * @since 2011-12-27 2589 | * @return boolean 2590 | */ 2591 | private function validate_options($options){ 2592 | //$options 2593 | if ($options != NULL && ! is_array ( $options )) { 2594 | throw new OSS_Exception ($options.':'.OSS_OPTIONS_MUST_BE_ARRAY); 2595 | } 2596 | } 2597 | 2598 | /** 2599 | * 检测上传文件的内容 2600 | * @param array $options (Optional) 2601 | * @throws OSS_Exception 2602 | * @author xiaobing.meng@alibaba-inc.com 2603 | * @since 2011-12-27 2604 | * @return string 2605 | */ 2606 | private function validate_content($options){ 2607 | if(isset($options[self::OSS_CONTENT])){ 2608 | if($options[self::OSS_CONTENT] == '' || !is_string($options[self::OSS_CONTENT])){ 2609 | throw new OSS_Exception(OSS_INVALID_HTTP_BODY_CONTENT,'-600'); 2610 | } 2611 | }else{ 2612 | throw new OSS_Exception(OSS_NOT_SET_HTTP_CONTENT, '-601'); 2613 | } 2614 | } 2615 | 2616 | /** 2617 | * 验证文件长度 2618 | * @param array $options (Optional) 2619 | * @author xiaobing.meng@alibaba-inc.com 2620 | * @since 2011-12-27 2621 | * @return void 2622 | */ 2623 | private function validate_content_length($options){ 2624 | if(isset($options[self::OSS_LENGTH]) && is_numeric($options[self::OSS_LENGTH])){ 2625 | if( ! $options[self::OSS_LENGTH] > 0){ 2626 | throw new OSS_Exception(OSS_CONTENT_LENGTH_MUST_MORE_THAN_ZERO, '-602'); 2627 | } 2628 | }else{ 2629 | throw new OSS_Exception(OSS_INVALID_CONTENT_LENGTH, '-602'); 2630 | } 2631 | } 2632 | 2633 | /** 2634 | * 校验BUCKET/OBJECT/OBJECT GROUP是否为空 2635 | * @param string $name (Required) 2636 | * @param string $errMsg (Required) 2637 | * @throws OSS_Exception 2638 | * @author xiaobing.meng@alibaba-inc.com 2639 | * @since 2011-12-27 2640 | * @return void 2641 | */ 2642 | private function is_empty($name,$errMsg){ 2643 | if(empty($name)){ 2644 | throw new OSS_Exception($errMsg); 2645 | } 2646 | } 2647 | 2648 | /** 2649 | * 设置http header 2650 | * @param string $key (Required) 2651 | * @param string $value (Required) 2652 | * @param array $options (Required) 2653 | * @throws OSS_Exception 2654 | * @author xiaobing.meng@alibaba-inc.com 2655 | * @return void 2656 | */ 2657 | private static function set_options_header($key, $value, &$options) { 2658 | if (isset ( $options [self::OSS_HEADERS] )) { 2659 | if (! is_array ( $options [self::OSS_HEADERS] )) { 2660 | throw new OSS_Exception(OSS_INVALID_OPTION_HEADERS, '-600'); 2661 | } 2662 | } else { 2663 | $options [self::OSS_HEADERS] = array (); 2664 | } 2665 | 2666 | $options [self::OSS_HEADERS] [$key] = $value; 2667 | } 2668 | } 2669 | 2670 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /util/mimetypes.class.php: -------------------------------------------------------------------------------- 1 | 'application/vnd.android.package-archive', 10 | '3gp' => 'video/3gpp', 'ai' => 'application/postscript', 11 | 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 12 | 'aiff' => 'audio/x-aiff', 'asc' => 'text/plain', 13 | 'atom' => 'application/atom+xml', 'au' => 'audio/basic', 14 | 'avi' => 'video/x-msvideo', 'bcpio' => 'application/x-bcpio', 15 | 'bin' => 'application/octet-stream', 'bmp' => 'image/bmp', 16 | 'cdf' => 'application/x-netcdf', 'cgm' => 'image/cgm', 17 | 'class' => 'application/octet-stream', 18 | 'cpio' => 'application/x-cpio', 19 | 'cpt' => 'application/mac-compactpro', 20 | 'csh' => 'application/x-csh', 'css' => 'text/css', 21 | 'dcr' => 'application/x-director', 'dif' => 'video/x-dv', 22 | 'dir' => 'application/x-director', 'djv' => 'image/vnd.djvu', 23 | 'djvu' => 'image/vnd.djvu', 24 | 'dll' => 'application/octet-stream', 25 | 'dmg' => 'application/octet-stream', 26 | 'dms' => 'application/octet-stream', 27 | 'doc' => 'application/msword', 'dtd' => 'application/xml-dtd', 28 | 'dv' => 'video/x-dv', 'dvi' => 'application/x-dvi', 29 | 'dxr' => 'application/x-director', 30 | 'eps' => 'application/postscript', 'etx' => 'text/x-setext', 31 | 'exe' => 'application/octet-stream', 32 | 'ez' => 'application/andrew-inset', 'flv' => 'video/x-flv', 33 | 'gif' => 'image/gif', 'gram' => 'application/srgs', 34 | 'grxml' => 'application/srgs+xml', 35 | 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', 36 | 'hdf' => 'application/x-hdf', 37 | 'hqx' => 'application/mac-binhex40', 'htm' => 'text/html', 38 | 'html' => 'text/html', 'ice' => 'x-conference/x-cooltalk', 39 | 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 40 | 'ief' => 'image/ief', 'ifb' => 'text/calendar', 41 | 'iges' => 'model/iges', 'igs' => 'model/iges', 42 | 'jnlp' => 'application/x-java-jnlp-file', 'jp2' => 'image/jp2', 43 | 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 44 | 'jpg' => 'image/jpeg', 'js' => 'application/x-javascript', 45 | 'kar' => 'audio/midi', 'latex' => 'application/x-latex', 46 | 'lha' => 'application/octet-stream', 47 | 'lzh' => 'application/octet-stream', 48 | 'm3u' => 'audio/x-mpegurl', 'm4a' => 'audio/mp4a-latm', 49 | 'm4p' => 'audio/mp4a-latm', 'm4u' => 'video/vnd.mpegurl', 50 | 'm4v' => 'video/x-m4v', 'mac' => 'image/x-macpaint', 51 | 'man' => 'application/x-troff-man', 52 | 'mathml' => 'application/mathml+xml', 53 | 'me' => 'application/x-troff-me', 'mesh' => 'model/mesh', 54 | 'mid' => 'audio/midi', 'midi' => 'audio/midi', 55 | 'mif' => 'application/vnd.mif', 'mov' => 'video/quicktime', 56 | 'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 57 | 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 58 | 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 59 | 'mpg' => 'video/mpeg', 'mpga' => 'audio/mpeg', 60 | 'ms' => 'application/x-troff-ms', 'msh' => 'model/mesh', 61 | 'mxu' => 'video/vnd.mpegurl', 'nc' => 'application/x-netcdf', 62 | 'oda' => 'application/oda', 'ogg' => 'application/ogg', 63 | 'ogv' => 'video/ogv', 'pbm' => 'image/x-portable-bitmap', 64 | 'pct' => 'image/pict', 'pdb' => 'chemical/x-pdb', 65 | 'pdf' => 'application/pdf', 66 | 'pgm' => 'image/x-portable-graymap', 67 | 'pgn' => 'application/x-chess-pgn', 'pic' => 'image/pict', 68 | 'pict' => 'image/pict', 'png' => 'image/png', 69 | 'pnm' => 'image/x-portable-anymap', 70 | 'pnt' => 'image/x-macpaint', 'pntg' => 'image/x-macpaint', 71 | 'ppm' => 'image/x-portable-pixmap', 72 | 'ppt' => 'application/vnd.ms-powerpoint', 73 | 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 74 | 'qti' => 'image/x-quicktime', 'qtif' => 'image/x-quicktime', 75 | 'ra' => 'audio/x-pn-realaudio', 76 | 'ram' => 'audio/x-pn-realaudio', 'ras' => 'image/x-cmu-raster', 77 | 'rdf' => 'application/rdf+xml', 'rgb' => 'image/x-rgb', 78 | 'rm' => 'application/vnd.rn-realmedia', 79 | 'roff' => 'application/x-troff', 'rtf' => 'text/rtf', 80 | 'rtx' => 'text/richtext', 'sgm' => 'text/sgml', 81 | 'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 82 | 'shar' => 'application/x-shar', 'silo' => 'model/mesh', 83 | 'sit' => 'application/x-stuffit', 84 | 'skd' => 'application/x-koan', 'skm' => 'application/x-koan', 85 | 'skp' => 'application/x-koan', 'skt' => 'application/x-koan', 86 | 'smi' => 'application/smil', 'smil' => 'application/smil', 87 | 'snd' => 'audio/basic', 'so' => 'application/octet-stream', 88 | 'spl' => 'application/x-futuresplash', 89 | 'src' => 'application/x-wais-source', 90 | 'sv4cpio' => 'application/x-sv4cpio', 91 | 'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 92 | 'swf' => 'application/x-shockwave-flash', 93 | 't' => 'application/x-troff', 'tar' => 'application/x-tar', 94 | 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 95 | 'texi' => 'application/x-texinfo', 96 | 'texinfo' => 'application/x-texinfo', 'tif' => 'image/tiff', 97 | 'tiff' => 'image/tiff', 'tr' => 'application/x-troff', 98 | 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', 99 | 'ustar' => 'application/x-ustar', 100 | 'vcd' => 'application/x-cdlink', 'vrml' => 'model/vrml', 101 | 'vxml' => 'application/voicexml+xml', 'wav' => 'audio/x-wav', 102 | 'wbmp' => 'image/vnd.wap.wbmp', 103 | 'wbxml' => 'application/vnd.wap.wbxml', 'webm' => 'video/webm', 104 | 'wml' => 'text/vnd.wap.wml', 105 | 'wmlc' => 'application/vnd.wap.wmlc', 106 | 'wmls' => 'text/vnd.wap.wmlscript', 107 | 'wmlsc' => 'application/vnd.wap.wmlscriptc', 108 | 'wmv' => 'video/x-ms-wmv', 'wrl' => 'model/vrml', 109 | 'xbm' => 'image/x-xbitmap', 'xht' => 'application/xhtml+xml', 110 | 'xhtml' => 'application/xhtml+xml', 111 | 'xls' => 'application/vnd.ms-excel', 112 | 'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 113 | 'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 114 | 'xul' => 'application/vnd.mozilla.xul+xml', 115 | 'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 116 | 'zip' => 'application/zip' ); 117 | 118 | public static function get_mimetype($ext) { 119 | return (isset ( self::$mime_types [$ext] ) ? self::$mime_types [$ext] : 'application/octet-stream'); 120 | } 121 | } 122 | --------------------------------------------------------------------------------