Fine Uploader 到 S3 存储桶收到 405 Method Not Allowed 错误
Posted
技术标签:
【中文标题】Fine Uploader 到 S3 存储桶收到 405 Method Not Allowed 错误【英文标题】:Fine Uploader to S3 bucket getting 405 Method Not Allowed error 【发布时间】:2013-10-20 01:56:39 【问题描述】:我一直把头撞在墙上,完全被难住了。我正在尝试使用 FineUploader 将文件直接上传到我的 Amazon S3 存储桶。我基本上已经从fineuploader.com 网页(将文件直接上传到Amazon S3)和服务器端php 中复制了代码。
当我尝试上传文件时,我看到到签名端点的帖子似乎工作成功,但是当它尝试上传到 S3 时,我收到 405“不允许的方法”错误。
HTML
<!DOCTYPE html>
<html >
<head >
<meta charset = "utf-8" >
<link href = "http://fineuploader.com/source/fineuploader-3.9.1.min.css" rel = "stylesheet" >
</head >
<body >
<div id = "fine-uploader" ></div >
<script src = "http://code.jquery.com/jquery-latest.js" ></script >
<script src = "js/uploader.js" ></script >
<script >
$(document).ready(function ()
$("#fine-uploader").fineUploaderS3(
debug: true,
request:
endpoint: 'upload.roughdrag.com',
accessKey: 'AKIAJL37USSCV......'
,
signature:
endpoint: 'handlers/uploadHandler.php'
,
uploadSuccess:
endpoint: 'index.php'
,
iframeSupport:
localBlankPagePath: 'blank.html'
,
retry:
enableAuto: true // defaults to false
,
paste:
targetElement: $(document),
promptForName: true
,
deleteFile:
enabled: true,
endpoint: 'handlers/uploadHandler.php'
);
);
</script >
</body >
</html >
PHP 签名端点 - uploadHandler.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('../../includes/functions.php');
use Aws\S3\S3Client;
// These assume you have the associated AWS keys stored in
// the associated system environment variables
$clientPrivateKey = 'removed';
// 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 = 'removed';
$serverPrivateKey = 'removed';
// The following variables are used when validating the policy document
// sent by the uploader:
$expectedBucketName = "upload.roughdrag.com";
// $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 = 5000000;
$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://www.roughdrag.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" => "Your 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'];
?>
Amazon S3 CORS 配置
<?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>
IAM 组安全政策
"Version":"2012-10-17",
"Statement":[
"Effect":"Allow",
"Action":"s3:PutObject",
"Resource":"arn:aws:s3:::upload.roughdrag.com/*"
]
uploader.js 来自http://fineuploader.com/source/all.fineuploader-3.9.1.min.js
控制台响应
[FineUploader 3.9.0-3] Grabbed 1 dropped files.
[FineUploader 3.9.0-3] Received 1 files or inputs.
[FineUploader 3.9.0-3] Submitting S3 signature request for 0
[FineUploader 3.9.0-3] Sending POST request for 0
POST http://www.roughdrag.com/handlers/uploadHandler.php 200 OK 195ms
[FineUploader 3.9.0-3] Sending upload request for 0
POST http://upload.roughdrag.com/ 405 Method Not Allowed 559ms
"NetworkError: 405 Method Not Allowed - http://upload.roughdrag.com/"
[FineUploader 3.9.0-3] Received response status 405 with body: <html>
<head><title>405 Method Not Allowed</title></head>
<body>
<h1>405 Method Not Allowed</h1>
<ul>
<li>Code: MethodNotAllowed</li>
<li>Message: The specified method is not allowed against this resource.</li>
<li>ResourceType: OBJECT</li>
<li>Method: POST</li>
<li>RequestId: 3493FE605B461EAF</li>
<li>HostId: HDXmtSpHufy6LDIH1Nsp0oYkLDvTC3XKFRRIadw66gmaMsF53Z3WYsCWooOoRcw2</li>
</ul>
<hr/>
</body>
</html>
[FineUploader 3.9.0-3] Waiting 5 seconds before retrying breakout.jpg...
[FineUploader 3.9.0-3] Detected valid cancel, retry, or delete click event on file 'breakout.jpg', ID: 0.
[FineUploader 3.9.0-3] Cancelling 0
该软件看起来很棒,但我就是无法通过它。任何帮助表示赞赏。
【问题讨论】:
【参考方案1】:我猜这是您将自定义域名映射到 S3 存储桶时创建的 DNS 问题。解析 upload.roughdrag.com 后,您似乎已将此 CNAME 映射到“upload.roughdrag.com.s3-website-us-east-1.amazonaws.com”。尝试将该 CNAME 映射到“upload.roughdrag.com.s3.amazaonaws.com”。
更新 1:
如果您在此更改后仍然看到问题,我会在AWS S3 forums 发帖。希望有工作人员回答。您的存储桶/CNAME 可能存在我无法看到的问题。看起来像是对 upload.roughdrag.com.s3.amazonaws.com 的 POST 请求,但是向 upload.roughdrag.com 发送 POST 请求时存在问题。我通过Postman 验证了这一点。
更新 2:
随着您最近的 CNAME 更改,S3 似乎正在接受 POST 请求。
【讨论】:
这就是问题所在,雷。 “静态网站托管”的存储桶属性将端点指定为upload.roughdrag.com.s3-website-us-east-1.amazonaws.com,但只要我将其更改为upload.roughdrag.com.s3.amazonaws。 com 这开始为我工作。非常感谢。 我的情况与原始海报非常相似。你能帮我分享一下你的 DNS 设置吗?而且,您是否使用 Amazon 作为 DNS?我只是将uploads.mydomain.com
映射到我的存储桶的端点BUCKETNAME.s3-website-us-east-1.amazonaws.com
。
"解析 upload.roughdrag.com 后,您似乎已将此 CNAME 映射到“upload.roughdrag.com.s3-website-us-east-1.amazonaws.com”。尝试映射该 CNAME 改为“upload.roughdrag.com.s3.amazaonaws.com””- 为我工作
小心那个错字,应该是:s3.amazonaws.com 不是 s3.amazaonaws.com【参考方案2】:
我有同样的问题。通过将区域添加到以下函数来修复它:
function getS3Client()
global $serverPublicKey, $serverPrivateKey;
return S3Client::factory(array(
'key' => $serverPublicKey,
'secret' => $serverPrivateKey,
'region' => 'ap-southeast-2' //Revomve from non Sydney bucket
));
通过单击属性查找存储桶的区域。然后静态网站托管,只从 ap-xxxxx-x 的端点获取详细信息
【讨论】:
以上是关于Fine Uploader 到 S3 存储桶收到 405 Method Not Allowed 错误的主要内容,如果未能解决你的问题,请参考以下文章
通过 ASP.NET 使用 S3 服务器端 CORS 的 Fine Uploader
Fine Uploader-没有收到来自已加载iframe的有效消息