首页 文章

无法使用新SDK上传到OneDrive

提问于
浏览
2

我正在将8.1应用程序转换为Windows 10 UWP . 该应用程序使用OneDrive进行私有备份和恢复,使用新的OneDrive SDK,因为LiveSDK不能与UWP应用程序一起使用 .

登录我的OneDrive,列出文件并下载它们没有问题(使用_1601118中显示的代码,但我还没有成功将文件上传到我的OneDrive .

我使用的代码是:

string path = "/EpubReader_Backups/" + zipfile.Name;
string s = "";
try
{
    Item uploadedItem = await App.Client
                  .Drive
                  .Root
                  .ItemWithPath(path)
                  .Content
                  .Request()
                  .PutAsync<Item>
                (await zipfile.OpenStreamForReadAsync());
}
catch (Exception ex)
{
    s = ex.Message;
}

再次按照GitHub文档中的说明进行操作 .

当进行调用时,应用程序开始上传流(大约30MB)但在大约30秒后引发异常(不是OneDriveException,而是正常异常) .

异常消息表明上传已被取消,但没有说明原因 .

错误消息(来自mscorlib)是“任务被取消” . 错误代码是-2146233029

到底是怎么回事?我究竟做错了什么?

4 回答

  • 0

    在等待SDK实现时,我发布了一个不可恢复的上传块到OneDrive

    using Microsoft.OneDrive.Sdk;
    using Newtonsoft.Json;
    using SimpleAuthenticationProvider;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Microsoft.OneDrive.Sdk
    {
    
        public static class Extend
        {
            public static async Task<Item> UploadChunks(this OneDriveClient client, string folder, string fileName, Stream stream, int chunkSize, LiveToken token)
            {
                var session = await client.Drive.Root.ItemWithPath(folder + "/" + fileName).CreateSession(new ChunkedUploadSessionDescriptor() { Name = fileName }).Request().PostAsync();
                using (OneDriveChunkUpload chunks = new OneDriveChunkUpload(stream, session.UploadUrl, chunkSize, token))
                {
                    return await client.Drive.Items[chunks.Upload().id].Request().GetAsync();
                }
            }
        }
        public class LiveToken
        {
            public string token_type { get; set; }
            public int expires_in { get; set; }
            public string scope { get; set; }
            public string access_token { get; set; }
            public string refresh_token { get; set; }
            public string user_id { get; set; }
        }
    
        public class itemCreated
        {
            public string id { get; set; }
            public string name { get; set; }
            public int size { get; set; }
        }
    
        class responseChunk
        {
            public string uploadUrl { get; set; }
            public DateTime expirationDateTime { get; set; }
            public List<string> nextExpectedRanges { get; set; }
            public List<Range> Ranges
            {
                get
                {
                    List<Range> ret = new List<Range>();
                    foreach (var r in nextExpectedRanges)
                    {
                        string[] parsed = r.Split('-');
                        Range ra = new Range(parsed[0], parsed[1]);
                        ret.Add(ra);
                    }
                    return ret;
                }
            }
        }
        class Range
        {
            public Range() { }
            public Range(string start, string end)
            {
                Start = int.Parse(start);
                if (!string.IsNullOrEmpty(end))
                    End = int.Parse(end);
            }
            public int Start { get; set; }
            public int? End { get; set; }
        }
        public class OneDriveChunkUpload : IDisposable
        {
            Stream source;
            string Url;
            int ChunkSize;
            responseChunk lastResponse;
            LiveToken Token;
            public itemCreated item = null;
            public OneDriveChunkUpload(Stream str, string url, int chunkSize, LiveToken token)
            {
                Token = token;
                source = str;
                Url = url;
                ChunkSize = chunkSize;
                lastResponse = new responseChunk() { nextExpectedRanges = new List<string>() { "0-" + (str.Length -1).ToString() } };
            }
    
            public itemCreated Upload()
            {
                long position = 0;
                while (lastResponse != null)
                {
                    Range r = new Range();
                    r.Start = lastResponse.Ranges[0].Start;
                    r.End = (int)Math.Min(((lastResponse.Ranges[0].End ?? int.MinValue) + 1) - r.Start, ChunkSize);
                    r.End += r.Start -1;
                    byte[] buffer = new byte[r.End.Value - r.Start + 1];
                    source.Position = r.Start;
                    //source.Seek(r.Start, SeekOrigin.Begin);
                    position += source.Read(buffer, 0, buffer.Length);
                    Put(buffer, r);
                }
                return item;
            }
    
            void Put(byte[] bytes, Range r)
            {
    
                WebRequest req = HttpWebRequest.Create(Url);
                req.Method = "PUT";
                req.Headers.Add(HttpRequestHeader.Authorization, string.Format("bearer {0}", Token.access_token));
                req.ContentLength = bytes.Length;
                string ContentRange = string.Format("bytes {0}-{1}/{2}", r.Start, r.End.Value, source.Length);
                req.Headers.Add(HttpRequestHeader.ContentRange, ContentRange);
                Stream requestStream = req.GetRequestStream();
                requestStream.Write(bytes, 0, bytes.Length);
                HttpWebResponse res = null;
                try
                {
                    res = (HttpWebResponse)req.GetResponse();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                string responseBody = null;
    
                using (StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
                {
                    responseBody = sr.ReadToEnd();
                }
                if (res.StatusCode == HttpStatusCode.Accepted)
                {
                    lastResponse = JsonConvert.DeserializeObject<responseChunk>(responseBody);
                }
                else if (res.StatusCode == HttpStatusCode.Created)
                {
                    lastResponse = null;
                    item = JsonConvert.DeserializeObject<itemCreated>(responseBody);
                }
                else
                {
                    throw new Exception("bad format");
                }
            }
    
            public void Dispose()
            {
                WebRequest req = HttpWebRequest.Create(Url);
                req.Method = "DELETE";
                WebResponse res = req.GetResponse();
            }
        }
    
    }
    
  • 0

    zipfile对象是使用Framework 4.5和4.6 ZipArchiveEntry类创建的简单zip,代码如下:

    public static async Task<StorageFile> ZipFileFromFiles(List<StorageFile> files)
            {
                DatabaseUtilities.CloseDatabases();
                using (MemoryStream zipStream = new MemoryStream())
                {
                    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
                    {
                        foreach (StorageFile file in files)
                        {
                            byte[] data;
                            ZipArchiveEntry entry = zip.CreateEntry(file.Name);
                            using (Stream zipFile = entry.Open())
                            {
                                data = await ReadFileBytes(file);
                                zipFile.Write(data, 0, data.Length);
                            }
                        }
    
                        await zipStream.FlushAsync();
                    }
    
                    byte[] zipfile = zipStream.ToArray();
                    String ss = "Backup from ";
                    ss += DateTime.Now.Year.ToString() + "-";
                    ss += DateTime.Now.Month.ToString("D2") + "-";
                    ss += DateTime.Now.Day.ToString("D2") + " ";
                    ss += DateTime.Now.Hour.ToString("D2") + "H";
                    ss += DateTime.Now.Minute.ToString("D2") + "M";
                    ss += DateTime.Now.Second.ToString("D2") + "S";
                    await WriteFile(App.Folder, ss + ".zip", zipfile);
                    return await App.Folder.GetFileAsync(ss + ".zip");
                }
            }
    

    此代码在应用程序的8.1版本中工作正常,它还在Windows 10版本中创建了正确的zip文件(我已分别检查了生成的zip文件) .

    是的,我同意你的意见,可能是报告错误的超时 .

    虽然正确报告超时很重要,但在我看来,如果要对Windows应用商店应用开发人员有用,则必须将某些功能添加到SDK:

    • 允许为上载文件设置超时

    • 允许向应用报告下载/上传的进度

    虽然这些功能似乎存在于REST Api中,但它们在c#SDK中是众所周知的(或者几乎不存在的文档可能没有提到它们) . 结果,用户体验相当令人沮丧,进度环转动和转动而没有任何进度信息被传达给app用户 .

    PS我已经修改了try块,如下所示,以测试zip流的await是否导致错误 . 该尝试现在读取:try {Stream strm = await zipfile.OpenStreamForReadAsync(); item uploadedItem = await App .Client .Drive .Root .ItemWithPath(path).Content .Request() . PutAsync(strm); }

    await zipfile.OpenStreamForReadAsync();语句正确执行,不会引发异常;导致问题的声明确实是将流上传到OneDrive .

  • 0

    我已经确定问题是在sdk上传代码中超时了 . 我已经逐渐减小了原始70Mb的zipfile大小,并且在该大小达到17Mb到18Mb之前给出了错误 . 在此之下,文件上传没有任何错误 . 我正在使用具有最大2Mbit上载功能的ADSL连接,因此这应该使开发团队能够 Build 所需的超时数量 . 但是,我必须说,在我看来,UWP应用程序应该在平板电脑和手机以及PC上运行,而且许多平板电脑和手机肯定会有一个SIM卡,并且可以上传和下载数据移动连接(街道上很少有光纤连接) . 因此,要么将超时值留给开发人员,要么在包中设置以平均连接速度(1Mb?)上载最大可用文件大小(100Mb?)的最大值 .

    我不打算批评SDK开发团队,但在我看来,在Windows 10的发布几个月后,在SDK的准备和文档中已经很少考虑,并且它在当前形式中是原始的 . . 等待更好的版本意味着应用程序不会发布到商店......

  • 1

    我知道这是一个令人沮丧的限制,我们正在努力 . 不幸的是,我们目前仅限于HttpClient的功能,它不允许任何粒度超时控制 . 目前最好的解决方案是最有可能实现resumable upload,特别是如果你想支持> 100 mb的文件 .

    SDK现在可以帮助解决这个问题 . 要创建分块上传会话:

    var uploadSession = await client
        .Drive
        .Root
        .ItemWithPath(path)
        .CreateSession(sessionDescriptor)
        .Request()
        .PostAsync();
    

    从那里你可以使用 uploadSession.UploadUrl 上传你的个别块 . await client.AuthenticationProvider.AppendAuthHeaderAsync(httpRequestMessage) 会将身份验证标头附加到每个块请求,因此您无需处理认证 .

    我们正在努力将其构建到SDK中,以便开发人员不必自己实现,但它应该有助于解决您的问题 .

相关问题