3

我现在正在尝试设置 Fineuploader-s3 以显示成功上传到 aws 服务器上的文件的图像,就像在此处的示例页面上所做的那样:http: //fineuploader.com/#s3-demo

我(仍然)使用https://github.com/Widen/fine-uploader-server/blob/master/php/s3/s3demo.php的代码,并且我已经添加

uploadSuccess: {
        endpoint: "s3demo.php?success"
    }

到我的javascript文件中的fine-uploader实例,以便临时链接应该由s3demo.php文件中的函数生成。

我意识到我必须安装 AWS SDK 才能让它工作。安装的 zip 方法真的不起作用,所以我使用 phar。我将 s3demo.php 文件的那部分更改为:

require 'aws.phar';
use Aws\S3\S3Client;

我还取消了对这两行的注释:

$serverPublicKey = $_SERVER['PARAM1'];
$serverPrivateKey = $_SERVER['PARAM2'];

我在让它工作时遇到了两个问题。首先是我从 AWS 的成功响应出现了问题,我认为我应该从中获取文件的链接。

文件上传完美,但我在控制台中收到错误:

[FineUploader 3.8.0] Sending POST request for 0 s3.jquery.fineuploader-3.8.0.js:164
[FineUploader 3.8.0] Received the following response body to an AWS upload success request for id 0: <br />
<b>Fatal error</b>:  Uncaught exception 'Guzzle\Http\Exception\CurlException' with message '[curl] 28: Connection timed out after 1001 milliseconds [url] http://169.254.169.254/latest/meta-data/iam/security-credentials/' in phar:///MYSITE/aws.phar/Guzzle/Http/Curl/CurlMulti.php:339
Stack trace:
#0 phar:///MYSITE//aws.phar/Guzzle/Http/Curl/CurlMulti.php(280): Guzzle\Http\Curl\CurlMulti-&gt;isCurlException(Object(Guzzle\Http\Message\Request), Object(Guzzle\Http\Curl\CurlHandle), Array)
#1 phar:///MYSITE//aws.phar/Guzzle/Http/Curl/CurlMulti.php(245): Guzzle\Http\Curl\CurlMulti-&gt;processResponse(Object(Guzzle\Http\Message\Request), Object(Guzzle\Http\Curl\CurlHandle), Array)
#2 phar:///MYSITE//aws.phar/Guzzle/Http/Curl/CurlMulti.php(228): Guzzle\Http\Curl\CurlMulti-&gt;processMessages()
#3 phar:///MYSITE//aws.phar/Guzzle/Http/Curl/CurlMulti.php(212): Guzzle\Http\Curl\CurlMulti-&gt;executeHandles()
#4 phar:///MYSITE/z/aw in <b>phar:///home/nextq2/public_html/lenz/aws.phar/Aws/Common/InstanceMetadata/InstanceMetadataClient.php</b> on line <b>82</b><br />
 s3.jquery.fineuploader-3.8.0.js:164
[FineUploader 3.8.0] Upload success was acknowledged by the server. s3.jquery.fineuploader-3.8.0.js:164

这是否意味着我的 AWS 开发工具包安装或我在 Amazon 上的权限设置有问题?对于 CORS 和 IAM 设置?其中仍然如下:

<CORSRule>
        <AllowedOrigin>MY WEBSITE</AllowedOrigin>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>

我对 IAM 的组策略:

    {
      "Version":"2012-10-17",
      "Statement":[{
         "Effect":"Allow",
         "Action":"s3:PutObject",
         "Resource":"arn:aws:s3:::MY_BUCKET/*”
       }]
}

第二个问题,我确信我应该能够弄清楚但不能,是如何在我的 javascript 中访问由 s3demo.php 生成的 json 数组,以便我可以显示上传的图像。我想这不是 $templink[0]。我想知道是否可以在http://fineuploader.com/#s3-demo上看到提供查看按钮功能的示例代码。如果我应该在这里提出第二个问题,我很乐意这样做。

非常感谢你花时间陪伴。

编辑按要求添加我的完整代码:

PHP:

<?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 = ‘I put my private key here;
// 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['PARAM1'];
$serverPrivateKey = $_SERVER['PARAM2'];

$expectedMaxSize = 15000000; 
$expectedBucket = “my bucket name here;

$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") {
  //  handlePreflightedRequest(); // 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') {
    handlePreflightedRequest();

    // 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 handlePreflightedRequest() {
    // If you are relying on CORS, you will need to adjust the allowed domain here.
    //header('Access-Control-Allow-Origin: http://nextquestion.org');
}

// Only needed in cross-origin setups
function handlePreflight() {
    handlePreflightedRequest();
    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 $expectedBucket;

    $pattern = "/\/$expectedBucket\/.+$/";
    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, $expectedBucket;

    $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 == $expectedBucket && $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)));
    }
}
function testfunction(){
    alert('whatever');
}
// 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'];
}
?>

我的html。我使用 Mark 在 StackOverflow 上的另一个示例进行此测试,因为最终我想同时提交一些其他数据:

<!DOCTYPE html>
<html>
<head>

  <title>test of fine uploader</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">


  <link href="fineuploader-3.8.0.css" rel="stylesheet">
  <style>
  .button {
      display: block;
      height: 30px;
      width: 100px;
      border: 1px solid #000;
  }
  </style>
 <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="s3.jquery.fineuploader-3.8.0.js"></script>
<script type="text/javascript" src="lenz_javascript4.js"></script>

</head> 
<body> 

<!-- Generated Image Thumbnail -->
<a href="#" id="thumbnail">view image</a>

<form action="fineuploadertest.php" method="post" id="uploader">
<input type="text" name="textbox" value="Test data">
    <div id="manual-fine-uploader"></div>
    <div id="triggerUpload" class="button" style="margin-top: 10px;">click here
    </div>
</form>

</body>
</html>

我的JavaScript:

$(document).ready(function() {

    $("#triggerUpload").click(function () {
        $("#manual-fine-uploader").fineUploaderS3('uploadStoredFiles'); 
    });

    function submitForm () { 
        if ($(this).fineUploader('getInProgress') == 0) {
            var failedUploads = $(this).fineUploaderS3('getUploads', 
                { status: qq.status.UPLOAD_FAILED });
            if (failedUploads.length == 0) {    
                // do any other form processing here
                $("#uploader").submit();
            }
        }
    };


    $("#manual-fine-uploader").fineUploaderS3({
        autoUpload: false,
        debug: true,

              request: {

                  endpoint: "http://my bucket name.s3.amazonaws.com",

                  accessKey: “I put my access key here”
              },
                validation: {
                    allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
                    sizeLimit: 15000000,
                    itemLimit: 3
                },

              signature: {

                  endpoint: "s3demo.php"
              },
            camera: {
                 ios: true
            },
              iframeSupport: {
                  localBlankPagePath: "/success.html"
              },
              uploadSuccess: {
        endpoint: "s3demo.php?success"

    }
    });
});
4

1 回答 1

4

听起来您只是想在 FineUploader.com 上反映 S3 演示的行为。因此,您显然遇到问题的部分是演示的一部分,它允许您查看/下载您上传的文件。我的猜测是您没有设置PARAM1andPARAM2环境变量。您应该真正了解一下$_SERVERPHP 文档中的 super global 是如何工作的。就目前而言,此代码期望您拥有一个名为的系统环境变量,该变量PARAM1保存与您应该为您的服务器(而不是您的客户端)使用的 IAM 用户关联的公共 AWS 密钥。系统PARAM2环境变量应设置为同一用户的密钥。您可以设置这些环境变量,也可以设置关联的$serverPublicKey$serverPrivateKeyPHP 分别对服务器端 IAM 用户的公钥和密钥进行全局设置。

请注意,与服务器的 AWS 公钥和密钥 (PARAM1PARAM2) 关联的系统环境变量的名称选择不佳是因为fineuploader.com S3 演示的服务器运行在由Amazon 的 Elastic Beanstalk创建的AWS EC2 实例上服务。Elastic Beanstalk 不提供(至少它显然不提供)通过 PHP 应用程序的 Elastic Beanstalk UI 命名系统环境变量的方法。它将它们命名为PARAM1,PARAM2等。

和变量不应是与您为客户端任务创建的 IAM 用户关联的相同键$serverPublicKey$serverPrivateKey您应该已创建具有适合服务器端任务的权限的不同 IAM 用户。例如,如果您想支持删除文件功能,您需要一个具有“S3:DeleteObject”权限的 IAM 用户。出于安全原因,此用户应仅限于服务器端任务。

在您的情况下,您的服务器端 IAM 用户必须对您的存储桶拥有“S3:GetObject”权限。需要此权限才能从您的存储桶中获取对象。最安全的方法是仅将此权限授予您的服务器端 IAM 用户。您可能会问:“如果我的客户端用户无法从我的存储桶中读取对象,我如何允许客户端下载文件?” 好吧,一种选择是将aclFine Uploader 中的选项设置为“public-read”,然后使用以下约定构造一个 URL 客户端:“ http://mybucket.s3.amazonaws.com/objectkey ”。这不是fineuploader.com 上的S3 演示的工作方式。请继续阅读以了解详细信息...

我不想让用户无限制地访问他们上传到 Fine Uploader 的 S3 存储桶中的文件,所以我将其保留acl为“私有”(默认值),我只给了我的服务器端 IAM 用户“S3:GetObject”权限Fine Uploader S3 存储桶,我让服务器向存储桶中的关联对象返回一个“定时炸弹”签名的 URL。服务器返回的 URL 包含一个过期参数,只允许使用 15 分钟。任何更改查询字符串中的过期参数的尝试都会使签名无效,并且请求将失败。PHP 示例中的getTempLink函数将生成一个定时炸弹签名的 URL,该 URL 在对 Fine Uploader 的uploadSucess.endpointPOST 请求的响应中返回。complete. responseJSON传递给回调的对象参数将包含一个tempLink包含签名 URL 的属性。然后,您可以生成一个将src属性设置为此属性值的锚点。

于 2013-08-22T02:37:21.150 回答