我在使用SAS(共享访问签名)从C ++写入Azure Block Blob时遇到问题。我正在使用Blob REST API和Poco。 HTTP请求返回错误404(资源不存在),但我无法弄清楚我做错了什么。
我用C#在服务器上生成SAS(似乎工作正常):
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("my-blob");
container.CreateIfNotExists();
SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(40);
sasConstraints.Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List;
string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);
return Request.CreateResponse(HttpStatusCode.OK, container.Uri + sasContainerToken);
在Azure门户中,我确实可以看到正在按预期创建Blob容器。我使用HTTP请求在C ++中收到此SAS。我得到的是这样的(出于安全原因,一些名称和签名被替换):
然后我尝试使用Poco和Blob REST API创建文件。看起来像这样:
std::string cloudUrl = sasURI + "&restype=container";
std::string fileName = "fname.ext";
Poco::URI* uri = new Poco::URI(cloudUrl.c_str());
std::string* path = new std::string(uri->getPathAndQuery());
Poco::Net::HTTPSClientSession* session = new Poco::Net::HTTPSClientSession(uri->getHost(), uri->getPort());
std::string method = Poco::Net::HTTPRequest::HTTP_PUT;
Poco::Net::HTTPRequest* request = new Poco::Net::HTTPRequest(method, *path, Poco::Net::HTTPMessage::HTTP_1_1);
request->add("x-ms-blob-content-disposition", "attachment; filename=\"" + fileName + "\"");
request->add("x-ms-blob-type", "BlockBlob");
request->add("x-ms-meta-m1", "v1");
request->add("x-ms-meta-m2", "v2");
Poco::Net::HTTPResponse* httpResponse = new Poco::Net::HTTPResponse();
int fileContent = 42;
request->setContentLength(sizeof(int));
request->setKeepAlive(true);
std::ostream& outputStream = session->sendRequest(*request);
outputStream << fileContent;
std::istream &is = session->receiveResponse(*httpResponse);
Poco::Net::HTTPResponse::HTTPStatus status = httpResponse->getStatus();
std::ostringstream outString;
Poco::StreamCopier::copyStream(is, outString);
if (status != Poco::Net::HTTPResponse::HTTP_OK)
{
Logger::log("Connection failed\nstatus:", status, "\nreason:", httpResponse->getReason(), "\nreasonForStatus:", httpResponse->getReasonForStatus(status), "\nresponseContent:", outString.str());
}
我查看了here REST API的工作原理。我发现here使用SAS时我不需要进行常规身份验证。
我在这里做错了什么?为什么我会收到错误404?
答案 0 :(得分:3)
我相信您的大多数代码都是正确的,您需要做的就是在SAS网址中插入文件名。
现在我已经更仔细地看到了这个问题,这就是正在发生的事情:
您正在blob容器(my-blob
)上创建SAS并使用此SAS上传文件(让我们称之为fname.ext
)。但是,您不在SAS URL中包含文件名,因此Azure Storage Service假定您尝试在my-blob
容器中上传名为$root
的文件,以便在服务上当Azure Blob服务尝试验证SAS时,它会根据$root
容器对其进行验证。由于您为my-blob
容器创建了SAS,而Azure服务正在使用$root
容器,因此SAS不匹配,这就是您收到403错误的原因。
您需要做的是在SAS URL中插入文件名。所以你的SAS URL(或请求URL)会是这样的(注意我在那里添加了fname.ext
):
https://myname.blob.core.windows.net/my-blob/的 fname.ext 强> SV = 2012-02-12&安培; SE = 2016-06-07T11%3A13%3A19Z&安培; SR = C&安培; SP =&WL放大器; SIG =% %%%%%%%%%%%%%%%%%%%%%%
此外,您不需要以下两行代码:
request->add("x-ms-version", "2015-02-21");
request->add("x-ms-date", "2016-06-07");
使用SAS时并不需要这些。
答案 1 :(得分:3)
我终于弄明白这里出了什么问题。 :)
上述代码中存在两个问题。第一个是需要将文件名插入到URL中,正如Gaurav Mantri解释的那样。这就是诀窍:
int indexOfQuestionMark = cloudUrl.find('?');
cloudUrl = cloudUrl.substr(0, indexOfQuestionMark) + "/" + fileName + cloudUrl.substr(indexOfQuestionMark);
另一个问题是我没有上传足够的字节。 sizeof(int)
是4个字节,而将42推入流将其转换为字符,使其仅为2个字节。然后服务器继续等待剩余的2个字节。这使得这是上面示例代码中的正确行:
request->setContentLength(2);
此外,它没有这三行,所以我认为他们不需要:
request->add("x-ms-blob-content-disposition", "attachment; filename=\"" + fileName + "\"");
request->add("x-ms-meta-m1", "v1");
request->add("x-ms-meta-m2", "v2");
同样,似乎还不需要添加此内容:"&restype=container"
。
最后,为了写入,SharedAccessBlobPermissions.List
权限不需要,因此可以在服务器端的SAS生成中省略这些权限。
答案 2 :(得分:1)
您的错误的一个可能原因可能是请求日期太旧。您今晚将请求日期设置为午夜UTC。 Azure存储允许大约15分钟的时钟偏差。请求日期/时间“太旧”是403错误的主要原因之一(除了不正确的帐户密钥和SAS的过期令牌)。
这是您设置x-ms-date
请求标头的方式。
request->add("x-ms-date", "2016-06-07");
此标头的值应采用以下格式进行格式化:
request->add("x-ms-date", "Sun, 11 Oct 2009 21:49:13 GMT");
通常在C#世界中,我们会使用DateTime.UtcNow.ToString("R")
来获取正确格式的日期/时间。
请相应更改您的代码,看看是否能解决问题。