使用Graph API将文件(>4MB)上传至OneDrive

5
我正在尝试使用Graph API上传文件到OneDrive。以下代码适用于上传小于4MB的文件,但是当我尝试上传大于4MB的文件时会显示错误。我阅读了这份文档,但仍不确定如何使其工作。
以下是适用于小于4MB文件的可用代码。
using (var client = new HttpClient())
{
    var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/content";
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());

    byte[] sContents = System.IO.File.ReadAllBytes(filePath);
    var content = new ByteArrayContent(sContents);

    var response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
}

请帮忙。
2个回答

12
我们需要根据长度(即4MB)拆分字节数组,并将它们传递给OneDrive API。下面是可工作的版本:
using (var client = new HttpClient())
{
    var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/createUploadSession";
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());

    var sessionResponse = client.PostAsync(apiUrl, null).Result.Content.ReadAsStringAsync().Result;

    byte[] sContents = System.IO.File.ReadAllBytes(filePath);
    var uploadSession = JsonConvert.DeserializeObject<UploadSessionResponse>(sessionResponse);
    string response = UploadFileBySession(uploadSession.uploadUrl, sContents);
}

UploadFileBySession的代码如下:
private string UploadFileBySession(string url, byte[] file)
{
    int fragSize = 1024 * 1024 * 4;
    var arrayBatches = ByteArrayIntoBatches(file, fragSize);
    int start = 0;
    string response = "";

    foreach (var byteArray in arrayBatches)
    {
        int byteArrayLength = byteArray.Length;
        var contentRange = " bytes " + start + "-" + (start + (byteArrayLength - 1)) + "/" + file.Length;

        using (var client = new HttpClient())
        {
            var content = new ByteArrayContent(byteArray);
            content.Headers.Add("Content-Length", byteArrayLength.ToString());
            content.Headers.Add("Content-Range", contentRange);

            response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
        }

        start = start + byteArrayLength;
    }
    return response;
}

internal IEnumerable<byte[]> ByteArrayIntoBatches(byte[] bArray, int intBufforLengt)
{
    int bArrayLenght = bArray.Length;
    byte[] bReturn = null;

    int i = 0;
    for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
    {
        bReturn = new byte[intBufforLengt];
        Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
        yield return bReturn;
    }

    int intBufforLeft = bArrayLenght - i * intBufforLengt;
    if (intBufforLeft > 0)
    {
        bReturn = new byte[intBufforLeft];
        Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
        yield return bReturn;
    }
}

创建上传会话时反序列化JSON响应的UploadSessionResponse类文件

public class UploadSessionResponse
{
    public string odatacontext { get; set; }
    public DateTime expirationDateTime { get; set; }
    public string[] nextExpectedRanges { get; set; }
    public string uploadUrl { get; set; }
}

什么是.ToProperString()?我在谷歌上找不到它。它在哪个库中? - Beau D'Amore
1
@BeauD'Amore 这是我应用程序中创建的一个扩展。我已将其更改为 "ToString()"。 - Mohamed Thaufeeq

6

如果文件大于4MB,您需要创建上传会话,并将其POST到此URL:

https://graph.microsoft.com/v1.0/me/drive/root:/{item-path}:/createUploadSession

传递一个包含项目的数组,

{
  "item": {
    "@odata.type": "microsoft.graph.driveItemUploadableProperties",
    "@microsoft.graph.conflictBehavior": "rename",
    "name": "largefile.dat"
  }
}

我使用"@microsoft.graph.conflictBehavior": "overwrite",而不是rename来避免冲突。响应将提供一个上传URL以分批上传文件。
{
  "uploadUrl": "https://sn3302.up.1drv.com/up/fe6987415ace7X4e1eF866337",
  "expirationDateTime": "2015-01-29T09:21:55.523Z"
}

我没有C#的例子,但这里有一个PHP的例子:

$url = $result['uploadUrl'];
$fragSize = 1024*1024*4;
$file = file_get_contents($source);
$fileSize = strlen($file);
$numFragments = ceil($fileSize / $fragSize);
$bytesRemaining = $fileSize;
$i = 0;
$response = null;

while ($i < $numFragments) {
    $chunkSize = $numBytes = $fragSize;
    $start = $i * $fragSize;
    $end = $i * $fragSize + $chunkSize - 1;
    $offset = $i * $fragSize;

    if ($bytesRemaining < $chunkSize) {
        $chunkSize = $numBytes = $bytesRemaining;
        $end = $fileSize - 1;
    }

    if ($stream = fopen($source, 'r')) {
        // get contents using offset
        $data = stream_get_contents($stream, $chunkSize, $offset);
        fclose($stream);
    }

    $contentRange = " bytes " . $start . "-" . $end . "/" . $fileSize;
    $headers = array(
        "Content-Length: $numBytes",
        "Content-Range: $contentRange"
    );

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    $server_output = curl_exec($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);

    $bytesRemaining = $bytesRemaining - $chunkSize;
    $i++;
}

非常感谢。我现在成功上传了大小超过4MB的文件。我现在将添加C#版本。 - Mohamed Thaufeeq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接