使用 Microsoft Graph 上传大型附件出现 invalid_token 错误
Uploading a large attachment using Microsoft Graph got invalid_token error
我正在尝试使用 Microsoft Graph Java SDK 2.10 将大型(> 4mb)附件上传到 Office 365 中的现有邮件。我遵循这些说明:https://docs.microsoft.com/en-us/graph/outlook-large-attachments?tabs=java
我已经成功创建了上传会话,并获得了一个类似于文档中示例的 uploadUrl 值。然后我使用 ChunkedUploadProvider 开始我对这个 url 的 PUT。
// Create an upload session
UploadSession uploadSession = client.me()
.messages(messageId).attachments()
.createUploadSession(attachmentItem)
.buildRequest()
.post();
ChunkedUploadProvider<AttachmentItem> chunkedUploadProvider =
new ChunkedUploadProvider<AttachmentItem>
(uploadSession, client, fileStream, attachmentItem.size, AttachmentItem.class);
// Config parameter is an array of integers
// customConfig[0] indicates the max slice size
// Max slice size must be a multiple of 320 KiB
int[] customConfig = { 12 * 320 * 1024 }; // still < 4MB as API recommended
// Do the upload
try {
chunkedUploadProvider.upload(callback, customConfig);
} catch (Exception e) {
log.error("Upload attachment file name {} for message id {}", fileAttachment.name, messageId, e);
}
我的问题是我收到 http 401(未授权)响应:
{
"error": {
"code": "InvalidMsaTicket",
"message": "ErrorCode: \u0027PP_E_RPS_CERT_NOT_FOUND\u0027. Message: \u0027 Internal error: spRPSTicket-\u003eProcessToken failed. Failed to call CRPSDataCryptImpl::UnpackData: Internal error: Failed to decrypt data. :Failed to get session key. RecipientId\u003d293577. spCache-\u003eGetCacheItem returns error.:Cert Name: (null). SKI: 3bd72187c709b1c40b994f8b496a5b9ebd2f9b0c...\u0027",
"innerError": {
"requestId": "7276a164-9c13-41cc-b46a-4a86303017a6",
"date": "2020-09-17T04:55:15"
}
}
}
我注意到创建上传会话的请求是:
https://graph.microsoft.com/v1.0/me/messages/AQMkADAwATM3ZmYAZS0xNWU2LTc4N1agAAAA==/attachments
而 uploadUrl 是:
https://outlook.office.com/api/v2.0/Users('00037ffe-15e6-787e-0000-00000')/Messages('AQMkADAwATM3ZmYAZS0xNVtUUgAAAA==')/AttachmentSessions('AQMkADAwwAAAA=')?authtoken=eyJhbGciOiJSUzI1NiIsImtpZCI6IktmYUNIUlN6bllHMmNIdDRobk9JQnpndlU5MD0iL
这是不同的 API(图表与 Outlook)。
我已经添加了邮件 read.write 范围,这样我就可以创建 < 4mb 的附件。我试图在获取访问令牌时将“https://outlook.office.com/Mail.ReadWrite”放入范围,但遇到了相同的 invalid_token 问题。我应该怎么做才能解决这个问题?
如有任何帮助,我们将不胜感激。谢谢。
我的错。请求不应包含授权 header:
Do not specify an Authorization request header. The PUT query uses a pre-authenticated URL from the uploadUrl property, that allows access to the https://outlook.office.com domain.
删除了授权header,请求正常工作。
只需对那个 URL 使用普通的 cURL PUT 请求,它就会工作...在 PHP 中它会是这样的(使用 php-curl-class/php-curl-class 和 microsoft/microsoft-graph 作曲家库):
function uploadLargeFileData(Graph $graph, string $messageId, string $fileName, string $fileContentType, string $fileData) : bool {
$fileSize = strlen($fileData);
// create upload session
$attachmentItem = (new AttachmentItem())
->setAttachmentType('file')
->setName($fileName)
->setSize($fileSize)
->setContentType($fileContentType);
/** @var UploadSession $uploadSession */
try {
$uploadSession = $graph->createRequest('POST', "/me/messages/{$messageId}/attachments/createUploadSession")
->addHeaders(['Content-Type' => 'application/json'])
->attachBody(['AttachmentItem' => $attachmentItem])
->setReturnType(UploadSession::class)
->execute();
}
catch (GraphException $e) {
return false;
}
$fileData = str_split($fileData, self::LargeFileChunkSize);
$fileDataCount = count($fileData);
foreach ($fileData as $chunkIndex => $chunkData) {
$chunkSize = strlen($chunkData);
$rangeStart = $chunkIndex * self::LargeFileChunkSize;
$rangeEnd = $rangeStart + $chunkSize - 1;
try {
// we need to use this plain access, because calling the API via the Graph provider leads to strange error of invalid audience
$curl = new Curl();
$curl->setHeaders([
'Content-Length' => $chunkSize,
'Content-Range' => "bytes {$rangeStart}-{$rangeEnd}/{$fileSize}",
'Content-Type' => 'application/octet-stream',
]);
$curl->put($uploadSession->getUploadUrl(), $chunkData);
if ($chunkIndex < $fileDataCount - 1 && $curl->httpStatusCode != 200) { // partial are responding with 200
return false;
}
elseif ($chunkIndex == $fileDataCount - 1 && $curl->httpStatusCode != 201) { // last response should have code 201
return false;
}
}
catch (\Exception $e) {
return false;
}
}
return true;
}
我正在尝试使用 Microsoft Graph Java SDK 2.10 将大型(> 4mb)附件上传到 Office 365 中的现有邮件。我遵循这些说明:https://docs.microsoft.com/en-us/graph/outlook-large-attachments?tabs=java
我已经成功创建了上传会话,并获得了一个类似于文档中示例的 uploadUrl 值。然后我使用 ChunkedUploadProvider 开始我对这个 url 的 PUT。
// Create an upload session
UploadSession uploadSession = client.me()
.messages(messageId).attachments()
.createUploadSession(attachmentItem)
.buildRequest()
.post();
ChunkedUploadProvider<AttachmentItem> chunkedUploadProvider =
new ChunkedUploadProvider<AttachmentItem>
(uploadSession, client, fileStream, attachmentItem.size, AttachmentItem.class);
// Config parameter is an array of integers
// customConfig[0] indicates the max slice size
// Max slice size must be a multiple of 320 KiB
int[] customConfig = { 12 * 320 * 1024 }; // still < 4MB as API recommended
// Do the upload
try {
chunkedUploadProvider.upload(callback, customConfig);
} catch (Exception e) {
log.error("Upload attachment file name {} for message id {}", fileAttachment.name, messageId, e);
}
我的问题是我收到 http 401(未授权)响应:
{
"error": {
"code": "InvalidMsaTicket",
"message": "ErrorCode: \u0027PP_E_RPS_CERT_NOT_FOUND\u0027. Message: \u0027 Internal error: spRPSTicket-\u003eProcessToken failed. Failed to call CRPSDataCryptImpl::UnpackData: Internal error: Failed to decrypt data. :Failed to get session key. RecipientId\u003d293577. spCache-\u003eGetCacheItem returns error.:Cert Name: (null). SKI: 3bd72187c709b1c40b994f8b496a5b9ebd2f9b0c...\u0027",
"innerError": {
"requestId": "7276a164-9c13-41cc-b46a-4a86303017a6",
"date": "2020-09-17T04:55:15"
}
}
}
我注意到创建上传会话的请求是:
https://graph.microsoft.com/v1.0/me/messages/AQMkADAwATM3ZmYAZS0xNWU2LTc4N1agAAAA==/attachments
而 uploadUrl 是:
https://outlook.office.com/api/v2.0/Users('00037ffe-15e6-787e-0000-00000')/Messages('AQMkADAwATM3ZmYAZS0xNVtUUgAAAA==')/AttachmentSessions('AQMkADAwwAAAA=')?authtoken=eyJhbGciOiJSUzI1NiIsImtpZCI6IktmYUNIUlN6bllHMmNIdDRobk9JQnpndlU5MD0iL
这是不同的 API(图表与 Outlook)。
我已经添加了邮件 read.write 范围,这样我就可以创建 < 4mb 的附件。我试图在获取访问令牌时将“https://outlook.office.com/Mail.ReadWrite”放入范围,但遇到了相同的 invalid_token 问题。我应该怎么做才能解决这个问题?
如有任何帮助,我们将不胜感激。谢谢。
我的错。请求不应包含授权 header:
Do not specify an Authorization request header. The PUT query uses a pre-authenticated URL from the uploadUrl property, that allows access to the https://outlook.office.com domain.
删除了授权header,请求正常工作。
只需对那个 URL 使用普通的 cURL PUT 请求,它就会工作...在 PHP 中它会是这样的(使用 php-curl-class/php-curl-class 和 microsoft/microsoft-graph 作曲家库):
function uploadLargeFileData(Graph $graph, string $messageId, string $fileName, string $fileContentType, string $fileData) : bool {
$fileSize = strlen($fileData);
// create upload session
$attachmentItem = (new AttachmentItem())
->setAttachmentType('file')
->setName($fileName)
->setSize($fileSize)
->setContentType($fileContentType);
/** @var UploadSession $uploadSession */
try {
$uploadSession = $graph->createRequest('POST', "/me/messages/{$messageId}/attachments/createUploadSession")
->addHeaders(['Content-Type' => 'application/json'])
->attachBody(['AttachmentItem' => $attachmentItem])
->setReturnType(UploadSession::class)
->execute();
}
catch (GraphException $e) {
return false;
}
$fileData = str_split($fileData, self::LargeFileChunkSize);
$fileDataCount = count($fileData);
foreach ($fileData as $chunkIndex => $chunkData) {
$chunkSize = strlen($chunkData);
$rangeStart = $chunkIndex * self::LargeFileChunkSize;
$rangeEnd = $rangeStart + $chunkSize - 1;
try {
// we need to use this plain access, because calling the API via the Graph provider leads to strange error of invalid audience
$curl = new Curl();
$curl->setHeaders([
'Content-Length' => $chunkSize,
'Content-Range' => "bytes {$rangeStart}-{$rangeEnd}/{$fileSize}",
'Content-Type' => 'application/octet-stream',
]);
$curl->put($uploadSession->getUploadUrl(), $chunkData);
if ($chunkIndex < $fileDataCount - 1 && $curl->httpStatusCode != 200) { // partial are responding with 200
return false;
}
elseif ($chunkIndex == $fileDataCount - 1 && $curl->httpStatusCode != 201) { // last response should have code 201
return false;
}
}
catch (\Exception $e) {
return false;
}
}
return true;
}