精细上传无效的响应

时间:2013-09-24 00:34:02

标签: php cors fine-uploader

当前页面:http://www.typhooncloud.com/fineuploader

尝试上传简单的jpg文件时,收到错误:

'无效的政策文件或请求标题!'

尝试上传后,这是我的index.html,s3handler.php,CORS策略和控制台输出。

已经推荐了所有更改。

HTML:

    <!DOCTYPE html>
    <head>
        <link href="http://typhooncloud.com/fineuploader/fineuploader-3.9.0-3.css" rel="stylesheet">
    </head> 
    <body>

        <!-- The element where Fine Uploader will exist. -->
        <div id="fine-uploader">
       </div>

        <!-- jQuery version 1.10.x (if you are using the jQuery plugin -->
        <script src="http://code.jquery.com/jquery-1.10.2.min.js" type="text/javascript"></script>

        <!-- Fine Uploader-jQuery -->
        <script src="http://typhooncloud.com/fineuploader/s3.jquery.fineuploader-3.9.0-3.js"></script>

        <script type="text/javascript">
            $(document).ready(function ()
            {
                $('#fine-uploader').fineUploaderS3({
                    debug: true,

                    request: {
                        endpoint: "getalink1001.s3.amazonaws.com",
                        accessKey: "MYACCESSKEY"
                    },
                    signature: {
                        endpoint: "http://typhooncloud.com/fineuploader/s3handler.php"
                    },
                    uploadSuccess: {
                        endpoint: "http://typhooncloud.com/fineuploader/s3handler.php?success"
                    },
                    iframeSupport: {
                        localBlankPagePath: "http://typhooncloud.com/fineuploader/success.html"
                    },
                    validation: {
                        allowedExtensions: ["gif", "jpeg", "jpg", "png"],
                        acceptFiles: "image/gif, image/jpeg, image/png",
                        sizeLimit: 5000000,
                        itemLimit: 3
                    },
                    retry: {
                        showButton: true
                    },
                    chunking: {
                        enabled: true
                    },
                    resume: {
                        enabled: true
                    },
                    deleteFile: {
                        enabled: true,
                        method: "POST",
                        endpoint: "http://typhooncloud.com/fineuploader/s3handler.php"
                    },
                    paste: {
                        targetElement: $(document),
                        promptForName: true
                    }
                });
            });
        </script>

    </body>
    </html>

SERVER:

    <?php
    /**
     * PHP Server-Side Example for Fine Uploader S3.
     * Maintained by Widen Enterprises.
     *
     * Note: This is the exact server-side code used by the S3 example
     * on fineuploader.com.
     *
     * This example:
     *  - handles both CORS and non-CORS environments
     *  - handles delete file requests for both DELETE and POST methods
     *  - Performs basic inspections on the policy documents and REST headers before signing them
     *  - Ensures again the file size does not exceed the max (after file is in S3)
     *  - signs policy documents (simple uploads) and REST requests
     *    (chunked/multipart uploads)
     *
     * Requirements:
     *  - PHP 5.3 or newer
     *  - Amazon PHP SDK (only if utilizing the AWS SDK for deleting files or otherwise examining them)
     *
     * If you need to install the AWS SDK, see http://docs.aws.amazon.com/aws-sdk-php-2/guide/latest/installation.html.
     */

    // You can remove these two lines if you are not using Fine Uploader's
    // delete file feature
    require("aws/aws-autoloader.php");

    use Aws\S3\S3Client;


    // These assume you have the associated AWS keys stored in
    // the associated system environment variables
    $clientPrivateKey = $_SERVER['MYSECRETKEY'];
    // These two keys are only needed if the delete file feature is enabled
    // or if you are, for example, confirming the file size in a successEndpoint
    // handler via S3's SDK, as we are doing in this example.
    $serverPublicKey = $_SERVER['MYPRIVATEKEY'];
    $serverPrivateKey = $_SERVER['MYSECRETKEY'];

    // The following variables are used when validating the policy document
    // sent by the uploader: 
    $expectedBucketName = "getalink1001";
    // $expectedMaxSize is the value you set the sizeLimit property of the 
    // validation option. We assume it is `null` here. If you are performing
    // validation, then change this to match the integer value you specified
    // otherwise your policy document will be invalid.
    // http://docs.fineuploader.com/branch/develop/api/options.html#validation-option
    $expectedMaxSize = 15000000;

    $method = getRequestMethod();

    // This first conditional will only ever evaluate to true in a
    // CORS environment
    if ($method == 'OPTIONS') {
        handlePreflight();
    }
    // This second conditional will only ever evaluate to true if
    // the delete file feature is enabled
    else if ($method == "DELETE") {
        handleCorsRequest(); // only needed in a CORS environment
        deleteObject();
    }
    // This is all you really need if not using the delete file feature
    // and not working in a CORS environment
    else if ($method == 'POST') {
        handleCorsRequest();

        // Assumes the successEndpoint has a parameter of "success" associated with it,
        // to allow the server to differentiate between a successEndpoint request
        // and other POST requests (all requests are sent to the same endpoint in this example).
        // This condition is not needed if you don't require a callback on upload success.
        if (isset($_REQUEST["success"])) {
            verifyFileInS3();
        }
        else {
            signRequest();
        }
    }

    // This will retrieve the "intended" request method.  Normally, this is the
    // actual method of the request.  Sometimes, though, the intended request method
    // must be hidden in the parameters of the request.  For example, when attempting to
    // send a DELETE request in a cross-origin environment in IE9 or older, it is not
    // possible to send a DELETE request.  So, we send a POST with the intended method,
    // DELETE, in a "_method" parameter.
    function getRequestMethod() {
        global $HTTP_RAW_POST_DATA;

        // This should only evaluate to true if the Content-Type is undefined
        // or unrecognized, such as when XDomainRequest has been used to
        // send the request.
        if(isset($HTTP_RAW_POST_DATA)) {
          parse_str($HTTP_RAW_POST_DATA, $_POST);
        }

        if ($_POST['_method'] != null) {
            return $_POST['_method'];
        }

        return $_SERVER['REQUEST_METHOD'];
    }

    // Only needed in cross-origin setups
    function handleCorsRequest() {
        // If you are relying on CORS, you will need to adjust the allowed domain here.
        header('Access-Control-Allow-Origin: http://typhoonupload.com');
    }

    // Only needed in cross-origin setups
    function handlePreflight() {
        handleCorsRequest();
        header('Access-Control-Allow-Methods: POST');
        header('Access-Control-Allow-Headers: Content-Type');
    }

    function getS3Client() {
        global $serverPublicKey, $serverPrivateKey;

        return S3Client::factory(array(
            'key' => $serverPublicKey,
            'secret' => $serverPrivateKey
        ));
    }

    // Only needed if the delete file feature is enabled
    function deleteObject() {
        getS3Client()->deleteObject(array(
            'Bucket' => $_POST['bucket'],
            'Key' => $_POST['key']
        ));
    }

    function signRequest() {
        header('Content-Type: application/json');

        $responseBody = file_get_contents('php://input');
        $contentAsObject = json_decode($responseBody, true);
        $jsonContent = json_encode($contentAsObject);

        $headersStr = $contentAsObject["headers"];
        if ($headersStr) {
            signRestRequest($headersStr);
        }
        else {
            signPolicy($jsonContent);
        }
    }

    function signRestRequest($headersStr) {
        if (isValidRestRequest($headersStr)) {
            $response = array('signature' => sign($headersStr));
            echo json_encode($response);
        }
        else {
            echo json_encode(array("invalid" => true));
        }
    }

    function isValidRestRequest($headersStr) {
        global $expectedBucketName;

        $pattern = "/\/$expectedBucketName\/.+$/";
        preg_match($pattern, $headersStr, $matches);

        return count($matches) > 0;
    }

    function signPolicy($policyStr) {
        $policyObj = json_decode($policyStr, true);

        if (isPolicyValid($policyObj)) {
            $encodedPolicy = base64_encode($policyStr);
            $response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy));
            echo json_encode($response);
        }
        else {
            echo json_encode(array("invalid" => true));
        }
    }

    function isPolicyValid($policy) {
        global $expectedMaxSize, $expectedBucketName;

        $conditions = $policy["conditions"];
        $bucket = null;
        $parsedMaxSize = null;

        for ($i = 0; $i < count($conditions); ++$i) {
            $condition = $conditions[$i];

            if (isset($condition["bucket"])) {
                $bucket = $condition["bucket"];
            }
            else if (isset($condition[0]) && $condition[0] == "content-length-range") {
                $parsedMaxSize = $condition[2];
            }
        }

        return $bucket == $expectedBucketName && $parsedMaxSize == (string)$expectedMaxSize;
    }

    function sign($stringToSign) {
        global $clientPrivateKey;

        return base64_encode(hash_hmac(
                'sha1',
                $stringToSign,
                $clientPrivateKey,
                true
            ));
    }

    // This is not needed if you don't require a callback on upload success.
    function verifyFileInS3() {
        global $expectedMaxSize;

        $bucket = $_POST["bucket"];
        $key = $_POST["key"];

        // If utilizing CORS, we return a 200 response with the error message in the body
        // to ensure Fine Uploader can parse the error message in IE9 and IE8,
        // since XDomainRequest is used on those browsers for CORS requests.  XDomainRequest
        // does not allow access to the response body for non-success responses.
        if (getObjectSize($bucket, $key) > $expectedMaxSize) {
            // You can safely uncomment this next line if you are not depending on CORS
            //header("HTTP/1.0 500 Internal Server Error");
            deleteObject();
            echo json_encode(array("error" => "File is too big!"));
        }
        else {
            echo json_encode(array("tempLink" => getTempLink($bucket, $key)));
        }
    }

    // Provide a time-bombed public link to the file.
    function getTempLink($bucket, $key) {
        $client = getS3Client();
        $url = "{$bucket}/{$key}";
        $request = $client->get($url);

        return $client->createPresignedUrl($request, '+15 minutes');
    }

    function getObjectSize($bucket, $key) {
        $objInfo = getS3Client()->headObject(array(
                'Bucket' => $bucket,
                'Key' => $key
            ));
        return $objInfo['ContentLength'];
    }
    ?>

CORS buckey政策如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
        <CORSRule>
            <AllowedOrigin>*</AllowedOrigin>
            <AllowedMethod>POST</AllowedMethod>
            <AllowedMethod>PUT</AllowedMethod>
            <AllowedMethod>DELETE</AllowedMethod>
            <MaxAgeSeconds>3000</MaxAgeSeconds>
            <ExposeHeader>ETag</ExposeHeader>
            <AllowedHeader>*</AllowedHeader>
        </CORSRule>
    </CORSConfiguration>

Chrome控制台:

      [FineUploader 3.9.0-3] Received 1 files or inputs. s3.jquery.fineuploader-3.9.0-3.js:164
      [FineUploader 3.9.0-3] Submitting S3 signature request for 0 s3.jquery.fineuploader-3.9.0-3.js:164
      [FineUploader 3.9.0-3] Sending POST request for 0 s3.jquery.fineuploader-3.9.0-3.js:164
      [FineUploader 3.9.0-3] Invalid policy document or request headers! s3.jquery.fineuploader-3.9.0-3.js:169
      [FineUploader 3.9.0-3] Policy signing failed.  Invalid policy document or request headers! s3.jquery.fineuploader-3.9.0-3.js:169
      [FineUploader 3.9.0-3] Received response status 0 with body:  s3.jquery.fineuploader-3.9.0-3.js:164
      [FineUploader 3.9.0-3] Upload attempt for file ID 0 to S3 is complete s3.jquery.fineuploader-3.9.0-3.js:164

提琴手输出:

      POST http://typhooncloud.com/fineuploader/s3handler.php HTTP/1.1
      Host: typhooncloud.com
      Connection: keep-alive
      Content-Length: 287
      Origin: http://typhooncloud.com
      User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
      Content-Type: application/json; charset=UTF-8
      Accept: */*
      Referer: http://typhooncloud.com/fineuploader/
      Accept-Encoding: gzip,deflate,sdch
      Accept-Language: en-US,en;q=0.8
      Cookie: __utma=33700260.900109978.1379619256.1379708113.1380038992.6; __utmb=33700260.1.10.1380038992; __utmc=33700260; __utmz=33700260.1380038992.6.2.utmcsr=stackoverflow.com|utmccn=(referral)|utmcmd=referral|utmcct=/questions/18971119/fine-uploader-invalid-response
      AlexaToolbar-ALX_NS_PH: AlexaToolbar/alxg-3.2

      {"expiration":"2013-09-24T16:26:35.948Z","conditions":[{"acl":"private"},{"bucket":"getalink1001"},{"Content-Type":"image/gif"},{"success_action_status":"200"},{"key":"f7e280c4-8cae-4bd7-82c4-d19e71186def.gif"},{"x-amz-meta-qqfilename":"319.gif"},["content-length-range","0","5000000"]]}


      HTTP/1.1 200 OK
      Date: Tue, 24 Sep 2013 16:21:49 GMT
      Server: Apache
      Access-Control-Allow-Origin: http://typhoonupload.com
      Keep-Alive: timeout=5, max=100
      Connection: Keep-Alive
      Transfer-Encoding: chunked
      Content-Type: application/json

      10
      {"invalid":true}
      0

2 个答案:

答案 0 :(得分:2)

看起来当您复制并粘贴PHP示例服务器代码时,您忽略了调整某些值。在这种情况下,未修改handlePreflightedRequest方法以反映您的域。

应该是:

function handlePreflightedRequest() {
    // If you are relying on CORS, you will need to adjust the allowed domain here.
    header('Access-Control-Allow-Origin: http://typhooncloud.com');
}

此外,handlePreflightedRequest方法可能应重命名为handleCorsRequest以避免混淆。我刚刚在服务器仓库中完成了这个。但是,您并不需要这样做,因为它不会影响代码的行为。

答案 1 :(得分:1)

旁注。让我指出一个关键的区别。在javascript代码中编写sizeLimit: 5000000,但在PHP中编写$expectedMaxSize = 15000000。这些值必须完全匹配。