├── .editorconfig
├── .gitignore
├── App.config
├── App.xaml
├── App.xaml.cs
├── DateConverter.cs
├── Fap.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Handlers
├── HttpProgressEventArgs.cs
├── ProgressContent.cs
├── ProgressMessageHandler.cs
├── ProgressStream.cs
└── ProgressWriteAsyncResult.cs
├── Hclient.cs
├── LocalFile.cs
├── Login.xaml
├── Login.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── NumTextBox.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── README.md
├── SyncFile.xaml
├── SyncFile.xaml.cs
├── TaskInfo.cs
├── TaskMange.cs
├── WinAPI.cs
├── alipay.jpg
├── aliyundrive-Client-CSharp.csproj
├── aliyundrive-Client-CSharp.sln
├── aliyundrive
├── Hclient.cs
├── MsgBase.cs
├── Util.cs
├── databox.cs
├── file.cs
└── token.cs
├── favicon.ico
├── packages.config
└── windows.png
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{cs,vb}]
2 |
3 | # IDE0060: 删除未使用的参数
4 | dotnet_code_quality_unused_parameters = non_public:suggestion
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | obj/
3 | bin/
4 | packages/
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
22 |
23 |
24 |
34 |
35 |
36 |
54 |
55 |
70 |
71 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace aliyundrive_Client_CSharp
10 | {
11 | ///
12 | /// App.xaml 的交互逻辑
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/DateConverter.cs:
--------------------------------------------------------------------------------
1 | using aliyundrive_Client_CSharp.aliyundrive;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Data;
9 |
10 | namespace aliyundrive_Client_CSharp
11 | { public class DateConverter : IValueConverter
12 | {
13 | object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
14 | {
15 | try
16 | {
17 | switch (parameter)
18 | {
19 | case "FormatDate": return FormatDate(value.ToString());
20 | case "FileSize": return FileSize(value);
21 | }
22 | }
23 | catch { }
24 | return value;
25 | }
26 |
27 | object FormatDate(string value)
28 | {
29 | return DateTime.Parse(value).ToString("yyyy-MM-dd\r\nHH:mm:ss");
30 | }
31 | object FileSize(object value)
32 | {
33 | var v = value as info_file;
34 | if (v.type == "folder") return "目录";
35 | long size = v.size;
36 | String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB" };
37 | long mod = 1024;
38 | int i = 0;
39 | while (size >= mod)
40 | {
41 | size /= mod;
42 | i++;
43 | }
44 | return size + "\r\n"+ units[i];
45 | }
46 |
47 | object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
48 | {
49 | throw new NotImplementedException();
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/Fap.cs:
--------------------------------------------------------------------------------
1 | using aliyundrive_Client_CSharp.aliyundrive;
2 | using Newtonsoft.Json;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading.Tasks;
9 |
10 | namespace aliyundrive_Client_CSharp
11 | {
12 | class FapShare : MagBase
13 | {
14 | public string code { get; set; }
15 | public string message { get; set; }
16 | public string id { get; set; }
17 | }
18 | class Fap
19 | {
20 | public string ver { get; set; }
21 | public string des { get; set; }
22 | public string tab { get; set; }
23 | public string pwd { get; set; }
24 | public long expires { get; set; }
25 | public string id { get; set; }
26 | public string user { get; set; }
27 | public string flag { get; set; }
28 | public List list { get; set; }
29 | public static Fap FAP_get(string hex)
30 | {
31 | try
32 | {
33 | return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Util.Hex2Bin(hex)));
34 | }
35 | catch { }
36 | return null;
37 | }
38 | public static string FAP_set(Fap fap)
39 | {
40 | try
41 | {
42 | return Util.Bin2Hex(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(fap)));
43 | }
44 | catch { }
45 | return null;
46 | }
47 | public static void FAP_rapid_upload(Fap fap,string fid, Action invoke)
48 | {
49 | foreach (var item in fap.list)
50 | {
51 | //dir 所在相对当前路径的目录 格式为 一层目录(aaa/) 二层目录(aaa/bbb/)
52 | if (string.IsNullOrEmpty(item.dir))
53 | {
54 | invoke(() => {
55 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, Name = item.name, sha1 = item.sha1, size = item.size, parent_file_id = fid });
56 | });
57 | }
58 | else
59 | {
60 | Task.Run(async () => {
61 | try
62 | {
63 | var u = new upload();
64 | var ms = Regex.Matches(item.dir, "([^/]+?)/");
65 | if (ms.Count == 0) throw new Exception("目录错误");
66 |
67 | string _fid = fid;
68 | if (!AsyncTaskMange.Instance.ParentIDs.ContainsKey(item.dir))
69 | {
70 | foreach (Match m in ms)
71 | {
72 | Console.WriteLine($"fap创建目录[{item.dir}]:{m.Groups[1].Value}");
73 | _fid = await u.getfolder(_fid, m.Groups[1].Value);
74 | }
75 | AsyncTaskMange.Instance.ParentIDs[item.dir] = _fid;
76 | }
77 | _fid = AsyncTaskMange.Instance.ParentIDs[item.dir];
78 | invoke(() => {
79 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, Name = item.name, sha1 = item.sha1, size = item.size, parent_file_id = _fid });
80 | });
81 | }
82 | catch (Exception ex)
83 | {
84 | invoke(() => {
85 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, Status = 3, Name = item.name + $"[{ex.Error()}]", sha1 = item.sha1, size = item.size, parent_file_id = fid });
86 | });
87 | }
88 | });
89 | }
90 | }
91 |
92 | }
93 | }
94 | class FapInfo
95 | {
96 | public string name { get; set; }
97 | public string author { get; set; }
98 | public long size { get; set; }
99 | public string sha1 { get; set; }
100 | public string md5 { get; set; }
101 | public string c1 { get; set; }
102 | public string c2 { get; set; }
103 | public string c3 { get; set; }
104 | public string url { get; set; }
105 | public string tmp { get; set; }
106 | public string ext { get; set; }
107 | public string type { get; set; }
108 | public string mime { get; set; }
109 | public string dir { get; set; }
110 | public long created { get; set; }
111 | public long updated { get; set; }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
13 |
14 |
15 |
16 |
17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
18 |
19 |
20 |
21 |
22 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
23 |
24 |
25 |
26 |
27 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
28 |
29 |
30 |
31 |
32 | The order of preloaded assemblies, delimited with line breaks.
33 |
34 |
35 |
36 |
37 |
38 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
39 |
40 |
41 |
42 |
43 | Controls if .pdbs for reference assemblies are also embedded.
44 |
45 |
46 |
47 |
48 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
49 |
50 |
51 |
52 |
53 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
54 |
55 |
56 |
57 |
58 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
59 |
60 |
61 |
62 |
63 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
64 |
65 |
66 |
67 |
68 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
69 |
70 |
71 |
72 |
73 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
74 |
75 |
76 |
77 |
78 | A list of unmanaged 32 bit assembly names to include, delimited with |.
79 |
80 |
81 |
82 |
83 | A list of unmanaged 64 bit assembly names to include, delimited with |.
84 |
85 |
86 |
87 |
88 | The order of preloaded assemblies, delimited with |.
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
97 |
98 |
99 |
100 |
101 | A comma-separated list of error codes that can be safely ignored in assembly verification.
102 |
103 |
104 |
105 |
106 | 'false' to turn off automatic generation of the XML Schema file.
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/Handlers/HttpProgressEventArgs.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System.ComponentModel;
4 |
5 | namespace System.Net.Http.Handlers
6 | {
7 | ///
8 | /// Provides data for the events generated by .
9 | ///
10 | public class HttpProgressEventArgs : ProgressChangedEventArgs
11 | {
12 | ///
13 | /// Initializes a new instance of the with the parameters given.
14 | ///
15 | /// The percent completed of the overall exchange.
16 | /// Any user state provided as part of reading or writing the data.
17 | /// The current number of bytes either received or sent.
18 | /// The total number of bytes expected to be received or sent.
19 | public HttpProgressEventArgs(int progressPercentage, object userToken, int bytesTransferred, long? totalBytes)
20 | : base(progressPercentage, userToken)
21 | {
22 | BytesTransferred = bytesTransferred;
23 | TotalBytes = totalBytes;
24 | }
25 |
26 | ///
27 | /// Gets the current number of bytes transferred.
28 | ///
29 | public int BytesTransferred { get; private set; }
30 |
31 | ///
32 | /// Gets the total number of expected bytes to be sent or received. If the number is not known then this is null.
33 | ///
34 | public long? TotalBytes { get; private set; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Handlers/ProgressContent.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System.Diagnostics.Contracts;
4 | using System.IO;
5 | using System.Threading.Tasks;
6 |
7 | namespace System.Net.Http.Handlers
8 | {
9 | ///
10 | /// Wraps an inner in order to insert a on writing data.
11 | ///
12 | internal class ProgressContent : HttpContent
13 | {
14 | private readonly HttpContent _innerContent;
15 | private readonly ProgressMessageHandler _handler;
16 | private readonly HttpRequestMessage _request;
17 |
18 | public ProgressContent(HttpContent innerContent, ProgressMessageHandler handler, HttpRequestMessage request)
19 | {
20 | Contract.Assert(innerContent != null);
21 | Contract.Assert(handler != null);
22 | Contract.Assert(request != null);
23 |
24 | _innerContent = innerContent;
25 | _handler = handler;
26 | _request = request;
27 |
28 | innerContent.Headers.CopyTo(Headers);
29 | }
30 |
31 | protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
32 | {
33 | ProgressStream progressStream = new ProgressStream(stream, _handler, _request, response: null);
34 | return _innerContent.CopyToAsync(progressStream);
35 | }
36 |
37 | protected override bool TryComputeLength(out long length)
38 | {
39 | long? contentLength = _innerContent.Headers.ContentLength;
40 | if (contentLength.HasValue)
41 | {
42 | length = contentLength.Value;
43 | return true;
44 | }
45 |
46 | length = -1;
47 | return false;
48 | }
49 |
50 | protected override void Dispose(bool disposing)
51 | {
52 | base.Dispose(disposing);
53 | if (disposing)
54 | {
55 | _innerContent.Dispose();
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Handlers/ProgressMessageHandler.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace System.Net.Http.Handlers
7 | {
8 | ///
9 | /// The provides a mechanism for getting progress event notifications
10 | /// when sending and receiving data in connection with exchanging HTTP requests and responses.
11 | /// Register event handlers for the events and
12 | /// to see events for data being sent and received.
13 | ///
14 | public class ProgressMessageHandler : DelegatingHandler
15 | {
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | public ProgressMessageHandler()
20 | {
21 | }
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The inner handler to which this handler submits requests.
27 | public ProgressMessageHandler(HttpMessageHandler innerHandler)
28 | : base(innerHandler)
29 | {
30 | }
31 |
32 | ///
33 | /// Occurs every time the client sending data is making progress.
34 | ///
35 | public event EventHandler HttpSendProgress;
36 |
37 | ///
38 | /// Occurs every time the client receiving data is making progress.
39 | ///
40 | public event EventHandler HttpReceiveProgress;
41 |
42 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
43 | {
44 | AddRequestProgress(request);
45 | return base.SendAsync(request, cancellationToken).Then(response => AddResponseProgress(request, response));
46 | }
47 |
48 | ///
49 | /// Raises the event.
50 | ///
51 | /// The request.
52 | /// The instance containing the event data.
53 | protected internal virtual void OnHttpRequestProgress(HttpRequestMessage request, HttpProgressEventArgs e)
54 | {
55 | if (HttpSendProgress != null)
56 | {
57 | HttpSendProgress(request, e);
58 | }
59 | }
60 |
61 | ///
62 | /// Raises the event.
63 | ///
64 | /// The request.
65 | /// The instance containing the event data.
66 | protected internal virtual void OnHttpResponseProgress(HttpRequestMessage request, HttpProgressEventArgs e)
67 | {
68 | if (HttpReceiveProgress != null)
69 | {
70 | HttpReceiveProgress(request, e);
71 | }
72 | }
73 |
74 | private void AddRequestProgress(HttpRequestMessage request)
75 | {
76 | if (HttpSendProgress != null && request != null && request.Content != null)
77 | {
78 | HttpContent progressContent = new ProgressContent(request.Content, this, request);
79 | request.Content = progressContent;
80 | }
81 | }
82 |
83 | private Task AddResponseProgress(HttpRequestMessage request, HttpResponseMessage response)
84 | {
85 | Task responseTask;
86 | if (HttpReceiveProgress != null && response != null && response.Content != null)
87 | {
88 | responseTask = response.Content.ReadAsStreamAsync().Then(
89 | stream =>
90 | {
91 | ProgressStream progressStream = new ProgressStream(stream, this, request, response);
92 | HttpContent progressContent = new StreamContent(progressStream);
93 | response.Content.Headers.CopyTo(progressContent.Headers);
94 | response.Content = progressContent;
95 | return response;
96 | }, runSynchronously: true);
97 | }
98 | else
99 | {
100 | responseTask = TaskHelpers.FromResult(response);
101 | }
102 |
103 | return responseTask;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Handlers/ProgressStream.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System.Diagnostics.Contracts;
4 | using System.IO;
5 | using System.Net.Http.Internal;
6 |
7 | namespace System.Net.Http.Handlers
8 | {
9 | ///
10 | /// This implementation of registers how much data has been
11 | /// read (received) versus written (sent) for a particular HTTP operation. The implementation
12 | /// is client side in that the total bytes to send is taken from the request and the total
13 | /// bytes to read is taken from the response. In a server side scenario, it would be the
14 | /// other way around (reading the request and writing the response).
15 | ///
16 | internal class ProgressStream : DelegatingStream
17 | {
18 | private readonly ProgressMessageHandler _handler;
19 | private readonly HttpRequestMessage _request;
20 |
21 | private int _bytesReceived;
22 | private long? _totalBytesToReceive;
23 |
24 | private int _bytesSent;
25 | private long? _totalBytesToSend;
26 |
27 | public ProgressStream(Stream innerStream, ProgressMessageHandler handler, HttpRequestMessage request, HttpResponseMessage response)
28 | : base(innerStream)
29 | {
30 | Contract.Assert(handler != null);
31 | Contract.Assert(request != null);
32 |
33 | if (request.Content != null)
34 | {
35 | _totalBytesToSend = request.Content.Headers.ContentLength;
36 | }
37 |
38 | if (response != null && response.Content != null)
39 | {
40 | _totalBytesToReceive = response.Content.Headers.ContentLength;
41 | }
42 |
43 | _handler = handler;
44 | _request = request;
45 | }
46 |
47 | public override int Read(byte[] buffer, int offset, int count)
48 | {
49 | int bytesRead = InnerStream.Read(buffer, offset, count);
50 | ReportBytesReceived(bytesRead, null);
51 | return bytesRead;
52 | }
53 |
54 | public override int ReadByte()
55 | {
56 | int byteRead = InnerStream.ReadByte();
57 | ReportBytesReceived(byteRead == -1 ? 0 : 1, null);
58 | return byteRead;
59 | }
60 |
61 | public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
62 | {
63 | return InnerStream.BeginRead(buffer, offset, count, callback, state);
64 | }
65 |
66 | public override int EndRead(IAsyncResult asyncResult)
67 | {
68 | int bytesRead = InnerStream.EndRead(asyncResult);
69 | ReportBytesReceived(bytesRead, asyncResult.AsyncState);
70 | return bytesRead;
71 | }
72 |
73 | public override void Write(byte[] buffer, int offset, int count)
74 | {
75 | InnerStream.Write(buffer, offset, count);
76 | ReportBytesSent(count, null);
77 | }
78 |
79 | public override void WriteByte(byte value)
80 | {
81 | InnerStream.WriteByte(value);
82 | ReportBytesSent(1, null);
83 | }
84 |
85 | public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
86 | {
87 | return new ProgressWriteAsyncResult(InnerStream, this, buffer, offset, count, callback, state);
88 | }
89 |
90 | public override void EndWrite(IAsyncResult asyncResult)
91 | {
92 | ProgressWriteAsyncResult.End(asyncResult);
93 | }
94 |
95 | internal void ReportBytesSent(int bytesSent, object userState)
96 | {
97 | if (bytesSent > 0)
98 | {
99 | _bytesSent += bytesSent;
100 | int percentage = 0;
101 | if (_totalBytesToSend.HasValue && _totalBytesToSend != 0)
102 | {
103 | percentage = (int)((100L * _bytesSent) / _totalBytesToSend);
104 | }
105 |
106 | // We only pass the request as it is guaranteed to be non-null (the response may be null)
107 | _handler.OnHttpRequestProgress(_request, new HttpProgressEventArgs(percentage, userState, _bytesSent, _totalBytesToSend));
108 | }
109 | }
110 |
111 | private void ReportBytesReceived(int bytesReceived, object userState)
112 | {
113 | if (bytesReceived > 0)
114 | {
115 | _bytesReceived += bytesReceived;
116 | int percentage = 0;
117 | if (_totalBytesToReceive.HasValue && _totalBytesToReceive != 0)
118 | {
119 | percentage = (int)((100L * _bytesReceived) / _totalBytesToReceive);
120 | }
121 |
122 | // We only pass the request as it is guaranteed to be non-null (the response may be null)
123 | _handler.OnHttpResponseProgress(_request, new HttpProgressEventArgs(percentage, userState, _bytesReceived, _totalBytesToReceive));
124 | }
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Handlers/ProgressWriteAsyncResult.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Diagnostics.Contracts;
5 | using System.IO;
6 | using System.Net.Http.Internal;
7 |
8 | namespace System.Net.Http.Handlers
9 | {
10 | internal class ProgressWriteAsyncResult : AsyncResult
11 | {
12 | private static readonly AsyncCallback _writeCompletedCallback = WriteCompletedCallback;
13 |
14 | private readonly Stream _innerStream;
15 | private readonly ProgressStream _progressStream;
16 | private readonly int _count;
17 |
18 | [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is handled as part of IAsyncResult completion.")]
19 | public ProgressWriteAsyncResult(Stream innerStream, ProgressStream progressStream, byte[] buffer, int offset, int count, AsyncCallback callback, object state)
20 | : base(callback, state)
21 | {
22 | Contract.Assert(innerStream != null);
23 | Contract.Assert(progressStream != null);
24 | Contract.Assert(buffer != null);
25 |
26 | _innerStream = innerStream;
27 | _progressStream = progressStream;
28 | _count = count;
29 |
30 | try
31 | {
32 | IAsyncResult result = innerStream.BeginWrite(buffer, offset, count, _writeCompletedCallback, this);
33 | if (result.CompletedSynchronously)
34 | {
35 | WriteCompleted(result);
36 | }
37 | }
38 | catch (Exception e)
39 | {
40 | Complete(true, e);
41 | }
42 | }
43 |
44 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is handled as part of IAsyncResult completion.")]
45 | private static void WriteCompletedCallback(IAsyncResult result)
46 | {
47 | if (result.CompletedSynchronously)
48 | {
49 | return;
50 | }
51 |
52 | ProgressWriteAsyncResult thisPtr = (ProgressWriteAsyncResult)result.AsyncState;
53 | try
54 | {
55 | thisPtr.WriteCompleted(result);
56 | }
57 | catch (Exception e)
58 | {
59 | thisPtr.Complete(false, e);
60 | }
61 | }
62 |
63 | private void WriteCompleted(IAsyncResult result)
64 | {
65 | _innerStream.EndWrite(result);
66 | _progressStream.ReportBytesSent(_count, AsyncState);
67 | Complete(result.CompletedSynchronously);
68 | }
69 |
70 | public static void End(IAsyncResult result)
71 | {
72 | AsyncResult.End(result);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Hclient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace aliyundrive_Client_CSharp.aliyundrive
10 | {
11 | class Hclient : HttpClient
12 | {
13 | public Hclient() : base()
14 | {
15 | DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
16 | }
17 | public async Task GetAsJsonAsync(string url)
18 | {
19 | var response = await GetAsync(url);
20 | if (response.IsSuccessStatusCode)
21 | return JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions
22 | {
23 | PropertyNameCaseInsensitive = true
24 | });
25 | else
26 | throw new HttpRequestException();
27 |
28 | }
29 | public async Task PostAsJsonAsync(string url, object data)
30 | {
31 | var response = await PostAsync(url, new StringContent(JsonSerializer.Serialize(data), System.Text.Encoding.UTF8, "application/json"));
32 | if (!response.IsSuccessStatusCode)
33 | throw new Exception();
34 |
35 | }
36 | public async Task PostAsJsonAsync(string url, object data)
37 | {
38 | var response = await PostAsync(url, new StringContent(JsonSerializer.Serialize(data), System.Text.Encoding.UTF8, "application/json"));
39 | if (response.IsSuccessStatusCode)
40 | {
41 | var content = await response.Content.ReadAsStringAsync();
42 | T d = JsonSerializer.Deserialize(content, new JsonSerializerOptions
43 | {
44 | PropertyNameCaseInsensitive = true
45 | });
46 | return d;
47 | }
48 | else
49 | throw new Exception(response.StatusCode + " " + await response.Content.ReadAsStringAsync());
50 |
51 | }
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/LocalFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace aliyundrive_Client_CSharp
9 | {
10 | class LocalFile
11 | {
12 | public static void Watch(string path, Action action)
13 | {
14 | FileListenerServer f1 = new FileListenerServer(path, action);
15 | f1.Start();
16 | }
17 |
18 | }
19 | public class FileListenerServer
20 | {
21 | private FileSystemWatcher _watcher;
22 | Action action;
23 | public FileListenerServer()
24 | {
25 | }
26 | public FileListenerServer(string path,Action action)
27 | {
28 | this.action = action;
29 | this._watcher = new FileSystemWatcher();
30 | _watcher.Path = path;
31 | _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.DirectoryName;
32 | _watcher.IncludeSubdirectories = true;
33 | _watcher.Created += FileWatcher_Created;
34 | _watcher.Changed += FileWatcher_Changed;
35 | _watcher.Deleted += FileWatcher_Deleted;
36 | _watcher.Renamed += FileWatcher_Renamed;
37 | _watcher.Error += _watcher_Error;
38 | }
39 |
40 | private void _watcher_Error(object sender, ErrorEventArgs e)
41 | {
42 | throw new NotImplementedException();
43 | }
44 |
45 | public void Start()
46 | {
47 |
48 | this._watcher.EnableRaisingEvents = true;
49 | Console.WriteLine("文件监控已经启动[{0}]", _watcher.Path);
50 | }
51 |
52 | public void Stop()
53 | {
54 |
55 | this._watcher.EnableRaisingEvents = false;
56 | this._watcher.Dispose();
57 | this._watcher = null;
58 | }
59 |
60 | protected void FileWatcher_Created(object sender, FileSystemEventArgs e)
61 | {
62 | Console.WriteLine("新增:" + e.ChangeType + ";" + e.FullPath + ";" + e.Name);
63 | action(e.Name,e.FullPath,e.ChangeType);
64 | }
65 | protected void FileWatcher_Changed(object sender, FileSystemEventArgs e)
66 | {
67 | Console.WriteLine("变更:" + e.ChangeType + ";" + e.FullPath + ";" + e.Name);
68 | action(e.Name, e.FullPath, e.ChangeType);
69 | }
70 | protected void FileWatcher_Deleted(object sender, FileSystemEventArgs e)
71 | {
72 | Console.WriteLine("删除:" + e.ChangeType + ";" + e.FullPath + ";" + e.Name);
73 | action(e.Name, e.FullPath, e.ChangeType);
74 | }
75 | protected void FileWatcher_Renamed(object sender, RenamedEventArgs e)
76 | {
77 | Console.WriteLine("重命名: OldPath:{0} NewPath:{1} OldFileName{2} NewFileName:{3}", e.OldFullPath, e.FullPath, e.OldName, e.Name);
78 | action(e.Name, e.FullPath, e.ChangeType);
79 | }
80 |
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Login.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Login.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Documents;
11 | using System.Windows.Input;
12 | using System.Windows.Media;
13 | using System.Windows.Media.Imaging;
14 | using System.Windows.Shapes;
15 | using System.Windows.Threading;
16 |
17 | namespace aliyundrive_Client_CSharp
18 | {
19 | ///
20 | /// Login.xaml 的交互逻辑
21 | ///
22 | public partial class Login : Window
23 | {
24 | public Login()
25 | {
26 | WinMethod.DisableMinmizebox(this);
27 | InitializeComponent();
28 | BrowserControl.Navigating += BrowserControl_Navigating;
29 | BrowserControl.LoadCompleted += BrowserControl_LoadCompleted;
30 | }
31 |
32 | private void BrowserControl_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
33 | {
34 | SuppressScriptErrors((WebBrowser)sender, true);
35 | }
36 |
37 | bool clear = true;
38 | private void BrowserControl_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
39 | {
40 | BrowserControl.InvokeScript("eval", "window.onerror=function(){return true}");
41 | try
42 | {
43 | if (clear)
44 | {
45 | clear = false;
46 | BrowserControl.InvokeScript("eval", "localStorage.clear()");
47 | }
48 | var o = BrowserControl.InvokeScript("eval", "localStorage.getItem('token')");
49 | if (o != DBNull.Value)
50 | {
51 | aliyundrive.token.SetToken(o.ToString());
52 | Close();
53 | }
54 | Console.WriteLine("内容:{0}", o);
55 | }
56 | catch (Exception ex)
57 | {
58 | Console.WriteLine("错误:{0}", ex.Message);
59 | }
60 | }
61 | public void SuppressScriptErrors(WebBrowser wb, bool Hide)
62 | {
63 | FieldInfo fiComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
64 | if (fiComWebBrowser == null) return;
65 |
66 | object objComWebBrowser = fiComWebBrowser.GetValue(wb);
67 | if (objComWebBrowser == null) return;
68 |
69 | objComWebBrowser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, null, objComWebBrowser, new object[] { Hide });
70 | }
71 |
72 | private void Btn_SetLogin_Click(object sender, RoutedEventArgs e)
73 | {
74 | aliyundrive.token.SetToken(Txt_token.Text);
75 | if (aliyundrive.token.Instance.refresh_token == "")
76 | {
77 | Txt_token.Text = "token格式错误,浏览器登录后,按F12打开控制台输入以下命令获取refresh_token:\r\nlocalStorage.getItem('token')";
78 | }
79 | else
80 | {
81 | Close();
82 | }
83 | }
84 | private void Btn_LoginOther_Click(object sender, RoutedEventArgs e)
85 | {
86 | Sp_Login.Visibility = Visibility.Hidden;
87 | BrowserControl.Visibility = Visibility.Visible;
88 | BrowserControl.Navigate("https://aliyundrive.com/sign/in");
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
12 |
13 |
14 |
15 |
16 | 退出登录
17 | 软件完全开源
18 | 查看源码
19 | 问题反馈
20 | 支持开源
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
87 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
157 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using aliyundrive_Client_CSharp.aliyundrive;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text.RegularExpressions;
9 | using System.Threading.Tasks;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Input;
13 | using System.Windows.Media.Imaging;
14 | using System.Windows.Threading;
15 |
16 | namespace aliyundrive_Client_CSharp
17 | {
18 | ///
19 | /// MainWindow.xaml 的交互逻辑
20 | /// PlainWizard
21 | /// https://github.com/PlainWizard/aliyundrive-Client-CSharp
22 | /// Bug反馈群:476304388
23 | ///
24 | public partial class MainWindow : Window
25 | {
26 | public MainWindow()
27 | {
28 | InitializeComponent();
29 | Task.Run(InitFile);
30 | Title += System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
31 | }
32 | private void Window_Loaded(object sender, RoutedEventArgs e)
33 | {
34 | InitClipboard();
35 | }
36 | public static string localRootDir = Environment.CurrentDirectory;
37 | void InitFile()
38 | {
39 | Dispatcher.Invoke(() =>
40 | {
41 | localRootDirectory.Text = "文件根目录:" + localRootDir;
42 | var task_MaxCountList = new List();
43 | for (var i = 1; i <= 100; i++) task_MaxCountList.Add(i);
44 | task_MaxCount.ItemsSource = task_MaxCountList;
45 | task_MaxCount.Text = "10";
46 | taskList.ItemsSource = TaskMange.Tasks;
47 | AsyncTaskMange.Instance.invoke = cb => { Dispatcher.Invoke(cb); };
48 | SyncButton.Background = AsyncTaskMange.Instance.Status ? System.Windows.Media.Brushes.Orange : null;
49 | AddDirectory(null);
50 | });
51 | TaskMange.OnTaskChange = OnTaskChange;
52 | TaskMange.OnServerFileChange = file_id =>
53 | {
54 | if (!ShowUserinfoAsync().Result) return;
55 | if (_currentDirectory_server.Count > 0)
56 | {
57 | if (_currentDirectory_server[_currentDirectory_server.Count - 1].file_id != file_id) return;
58 | }
59 | else if (file_id != "root") return;
60 | AddDirectory_server(null).Wait();
61 | };
62 | LocalFile.Watch(localRootDir, (name, path, type) =>
63 | {
64 | Console.WriteLine($"文件变动[{path}][{fullDirectory}]");
65 | if (fullDirectory == path.Substring(0, path.LastIndexOf('\\')))
66 | {
67 | Console.WriteLine("文件变动,重新绑定:[{0}]", path);
68 | Dispatcher.Invoke(() =>
69 | {
70 | UpdateShowFile();
71 | });
72 | }
73 | AsyncTaskMange.Instance.Add(path);
74 | });
75 | RefreshData();
76 | Sync();
77 | }
78 | void Sync()
79 | {
80 | if (!AsyncTaskMange.Instance.Status) return;
81 | var dir = new DirectoryInfo(MainWindow.localRootDir);
82 | foreach (var item in dir.GetFiles("*", SearchOption.AllDirectories))
83 | {
84 | AsyncTaskMange.Instance.Add(item.FullName);
85 | }
86 | }
87 | async void RefreshData()
88 | {
89 | if (!await ShowUserinfoAsync()) return;
90 | await AddDirectory_server(null);
91 | }
92 | async Task ShowUserinfoAsync()
93 | {
94 | if (token.Instance.refresh_token == "")
95 | {
96 | LoginOK = false;
97 | ShowServerTip($"token错误(点击右上角重新登录)");
98 | return false;
99 | }
100 | LoadingShow();
101 | var r = await databox.Instance.get_personal_info();
102 | LoadingHide();
103 | LoginOK = r.success;
104 | if (!r.success)
105 | {
106 | ShowServerTip($"需要登录(点击右上角登录):{r.Error}");
107 | return false;
108 | }
109 | var used_size = Util.GetShowSize(databox.Instance.personal_space_info.used_size);
110 | var total_size = Util.GetShowSize(databox.Instance.personal_space_info.total_size);
111 | var step = 100 * databox.Instance.personal_space_info.used_size / databox.Instance.personal_space_info.total_size;
112 | string userinfo = $@"欢迎您!{databox.Instance.personal_rights_info.name} [{token.Instance.nick_name}][{token.Instance.user_name}][{databox.Instance.personal_rights_info.spu_id}]
113 | 总空间:{total_size} 已使用:{used_size} 使用:{step}%
114 | ";
115 |
116 | Dispatcher.Invoke(() =>
117 | {
118 | localRootSpace.Value = step;
119 | localRootUserinfo.Text = userinfo;
120 | });
121 | return true;
122 | }
123 |
124 | public string rootDirectory { get { return _currentDirectory[0]; } set { _currentDirectory[0] = value; } }
125 | List _currentDirectory = new List { localRootDir };
126 | List _currentDirectory_server = new List { };
127 | public string fullDirectory { get; set; }
128 | public string serverDirectory_file_id { get; set; } = "root";
129 | public string relativeDirectory { get { return fullDirectory.Substring(rootDirectory.Length); } }
130 | void OnTaskChange()
131 | {
132 | Dispatcher.Invoke(() => { taskHeader.Header = TaskMange.GetHeadStr(); });
133 | }
134 | #region 本地文件区
135 | private void Button_Click_OpenDirectory(object sender, RoutedEventArgs e)
136 | {
137 | try
138 | {
139 | Process.Start(fullDirectory);
140 | }
141 | catch (Exception ex)
142 | {
143 | MessageBox.Show($"无法打开文件夹:{ex.Error()}");
144 | }
145 | }
146 | void UpdateShowFile()
147 | {
148 | List list = new List();
149 | var d = new System.IO.DirectoryInfo(fullDirectory);
150 | foreach (var item in d.GetDirectories())
151 | {
152 | list.Add(new info_file { type = "folder", name = item.Name, file_id = item.FullName, updated_at = item.LastWriteTime });
153 | }
154 | foreach (var item in d.GetFiles())
155 | {
156 | list.Add(new info_file { size = item.Length, name = item.Name, file_id = item.FullName, updated_at = item.LastWriteTime });
157 | //.ToString("yyyy-MM-dd HH:mm:sss")
158 | }
159 | Dispatcher.Invoke(() =>
160 | {
161 | localFile.ItemsSource = list;
162 | });
163 | }
164 |
165 | private void Button_Click_Upload(object sender, RoutedEventArgs e)
166 | {
167 | foreach (info_file item in localFile.SelectedItems)
168 | {
169 | //添加上传任务
170 | if (item.type == "folder") continue;
171 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, FullName = fullDirectory + "\\" + item.name, Name = item.name, parent_file_id = serverDirectory_file_id });
172 | }
173 | }
174 |
175 | private void localFile_MouseDoubleClick(object sender, MouseButtonEventArgs e)
176 | {
177 | if (localFile.SelectedItem == null) return;
178 | var f = (info_file)localFile.SelectedItem;
179 | Console.WriteLine($"localFile_MouseDoubleClick:{f.name}");
180 | if (f.type == "folder")
181 | {
182 | Console.WriteLine($"点击目录:{f.name}:{fullDirectory}");
183 | AddDirectory(f.name);
184 | }
185 | }
186 |
187 | public void SubDirectory(bool root = false)
188 | {
189 | if (_currentDirectory.Count != 1)
190 | {
191 | _currentDirectory.RemoveAt(_currentDirectory.Count - 1);
192 | while (root)
193 | {
194 | if (_currentDirectory.Count == 1) break;
195 | _currentDirectory.RemoveAt(_currentDirectory.Count - 1);
196 | }
197 | }
198 | fullDirectory = Path.Combine(_currentDirectory.ToArray());
199 | Dispatcher.Invoke(() =>
200 | {
201 | localFileDirectory.Text = relativeDirectory;
202 | });
203 | UpdateShowFile();
204 | }
205 | public void AddDirectory(string path)
206 | {
207 | if (!string.IsNullOrEmpty(path)) _currentDirectory.Add(path);
208 | fullDirectory = Path.Combine(_currentDirectory.ToArray());
209 | Dispatcher.Invoke(() =>
210 | {
211 | localFileDirectory.Text = relativeDirectory;
212 | });
213 | UpdateShowFile();
214 | }
215 |
216 | private void Button_Click_RootDirectory(object sender, RoutedEventArgs e)
217 | {
218 | SubDirectory(true);
219 | }
220 |
221 | private void Button_Click_SubDirectory(object sender, RoutedEventArgs e)
222 | {
223 | SubDirectory();
224 | }
225 |
226 | #endregion
227 | #region 服务器区
228 | private void Button_Click_Server_OpenDownUrl(object sender, RoutedEventArgs e)
229 | {
230 | foreach (info_file item in serverFile.SelectedItems)
231 | {
232 | Console.WriteLine($"OpenDownUrl:{item.name},{item.file_id},{item.download_url}");
233 | if (item.type == "folder") continue;
234 | new RefererDownload.MainWindow(item.download_url, "https://www.aliyundrive.com/").Show();
235 | break;
236 | }
237 | }
238 | private async void Button_Click_Server_Show(object sender, RoutedEventArgs e)
239 | {
240 | try
241 | {
242 | LoadingShow();
243 | Fap fap = new Fap() { tab = "aliyundrive", des = "", list = new List() };
244 | if (_currentDirectory_server.Count > 0)
245 | fap.des = _currentDirectory_server[_currentDirectory_server.Count - 1].name;
246 | foreach (info_file item in serverFile.SelectedItems.Cast().ToList())
247 | {
248 | if (item.type == "folder")
249 | {
250 | var f = new file();
251 | f.parent_file_id = item.file_id;
252 | f.drive_id = token.Instance.default_drive_id;
253 | f.limit = 1500;
254 | var r = await f.list();
255 | if (!r.Yes) throw new Exception(r.Error);
256 | var dir = item.name + "/";
257 | fap.des = item.name;
258 | foreach (var d in r.obj.items)
259 | {
260 | if (d.type != "folder")
261 | {
262 | fap.list.Add(new FapInfo
263 | {
264 | name = d.name
265 | ,
266 | type = d.type
267 | ,
268 | updated = d.updated_at.Ticks
269 | ,
270 | ext = d.file_extension
271 | ,
272 | size = d.size
273 | ,
274 | sha1 = d.content_hash
275 | ,
276 | c1 = d.content_hash_name
277 | ,
278 | c2 = d.crc64_hash
279 | ,
280 | dir = dir
281 | });
282 | }
283 | }
284 | }
285 | else
286 | {
287 | if (fap.des == "") fap.des = item.name;
288 | fap.list.Add(new FapInfo
289 | {
290 | name = item.name
291 | ,
292 | type = item.type
293 | ,
294 | updated = item.updated_at.Ticks
295 | ,
296 | ext = item.file_extension
297 | ,
298 | size = item.size
299 | ,
300 | sha1 = item.content_hash
301 | ,
302 | c1 = item.content_hash_name
303 | ,
304 | c2 = item.crc64_hash
305 | });
306 |
307 | }
308 | }
309 | if (fap.list.Count == 0) throw new Exception("没有文件");
310 | var h=new Hclient();
311 | var r2 = await h.GetAsJsonAsync("https://api.wyfxw.cn/api/file/fap?fap=" + Fap.FAP_set(fap));
312 | if (!r2.Yes||r2.obj.code!="200")
313 | {
314 | Dispatcher.Invoke(()=> {
315 | MessageBox.Show(r2.ok ? r2.obj.code : r2.Error);
316 | });
317 | return;
318 | }
319 | Process.Start("https://file.wyfxw.cn/?id=" + r2.obj.id);
320 | }
321 | catch (Exception ex)
322 | {
323 | MessageBox.Show(ex.Message);
324 | }
325 | finally
326 | {
327 | LoadingHide();
328 | }
329 | }
330 | private void Button_Click_Server_CopyDownUrl(object sender, RoutedEventArgs e)
331 | {
332 |
333 | try
334 | {
335 | string urls = "";
336 | if (serverFile.SelectedItems.Count == 1)
337 | {
338 | var item = ((info_file)serverFile.SelectedItems[0]);
339 | if (item.type == "folder") throw new Exception("暂时不支持复制目录,可以使用fap分享");
340 | urls = item.download_url;
341 | Console.WriteLine($"copy:{urls}");
342 | }
343 | else
344 | {
345 | foreach (info_file item in serverFile.SelectedItems)
346 | {
347 | Console.WriteLine($"copy:{item.name},{item.file_id},{item.download_url}");
348 | if (item.type == "folder") throw new Exception("暂时不支持复制目录,可以使用fap分享");
349 | urls += $"{item.name}:{item.download_url}\r\n";
350 | }
351 | }
352 |
353 | if (urls == "") throw new Exception("没有url");
354 | Clipboard.SetText(urls);
355 | }
356 | catch (Exception ex)
357 | {
358 | MessageBox.Show(ex.Message);
359 | }
360 | }
361 | private void Button_Click_Server_Share(object sender, RoutedEventArgs e)
362 | {
363 |
364 | try
365 | {
366 | List list = new List();
367 | string share_task_name = "";
368 | if (serverFile.SelectedItems.Count == 1)
369 | {
370 | var item = ((info_file)serverFile.SelectedItems[0]);
371 | //if (item.type == "folder") throw new Exception("暂时不支持分享目录,可以使用fap分享");
372 |
373 | Console.WriteLine($"Button_Click_Server_Share:{item.name},{item.file_id}");
374 |
375 | list.Add(item.file_id);
376 | share_task_name = item.name;
377 |
378 | }
379 | else
380 | {
381 |
382 | foreach (info_file item in serverFile.SelectedItems)
383 | {
384 | Console.WriteLine($"share:{item.name},{item.file_id}");
385 | // if (item.type == "folder") throw new Exception("暂时不支持分享目录,可以使用fap分享");
386 | list.Add(item.file_id);
387 | share_task_name = item.name;
388 | }
389 | share_task_name += $" 等{serverFile.SelectedItems.Count}个文件 ";
390 | }
391 | TaskMange.Add(new TaskInfo { Type = TaskType.分享, file_id_list = list, Name = share_task_name}) ;
392 |
393 | //if (urls == "") throw new Exception("没有url");
394 | //Clipboard.SetText(urls);
395 | }
396 | catch (Exception ex)
397 | {
398 | MessageBox.Show(ex.Message);
399 | }
400 | }
401 | void ShowServerTip(string msg)
402 | {
403 | Dispatcher.Invoke(() =>
404 | {
405 | if (string.IsNullOrEmpty(msg))
406 | {
407 | serverErorTip.Visibility = Visibility.Hidden;
408 | }
409 | else
410 | {
411 | serverErorTip.Text = msg;
412 | serverErorTip.Visibility = Visibility.Visible;
413 | }
414 | });
415 | }
416 | async Task UpdateShowFile_server(string file_id)
417 | {
418 | var f = new file();
419 | if (string.IsNullOrEmpty(file_id)) f.parent_file_id = "root";
420 | else f.parent_file_id = file_id;
421 | serverDirectory_file_id = f.parent_file_id;
422 | f.drive_id = token.Instance.default_drive_id;
423 | try
424 | {
425 | var r = await f.list();
426 | Dispatcher.Invoke(() =>
427 | {
428 | serverFile.ItemsSource = r.success ? r.obj.items : null;
429 | ShowServerTip(r.Error);
430 | });
431 |
432 | }
433 | catch (Exception ex)
434 | {
435 | Dispatcher.Invoke(() =>
436 | {
437 | ShowServerTip(ex.Message);
438 | });
439 | }
440 | }
441 |
442 | async Task SubDirectory_server(bool root = false)
443 | {
444 |
445 | if (root || _currentDirectory_server.Count <= 1)
446 | {
447 | _currentDirectory_server.Clear();
448 | await UpdateShowFile_server(null);
449 | }
450 | else
451 | {
452 | _currentDirectory_server.RemoveAt(_currentDirectory_server.Count - 1);
453 | await UpdateShowFile_server(_currentDirectory_server[_currentDirectory_server.Count - 1].file_id);
454 | }
455 | Dispatcher.Invoke(() =>
456 | {
457 | serverFileDirectory.Text = Path.Combine(_currentDirectory_server.Select(o => o.name).ToArray());
458 | });
459 | }
460 | async Task AddDirectory_server(info_file fileinfo)
461 | {
462 | if (fileinfo == null)
463 | {
464 | if (_currentDirectory_server.Count == 0)
465 | {
466 | await UpdateShowFile_server(null);
467 | }
468 | else
469 | {
470 | await UpdateShowFile_server(_currentDirectory_server[_currentDirectory_server.Count - 1].file_id);
471 | }
472 | }
473 | else
474 | {
475 | _currentDirectory_server.Add(fileinfo);
476 | UpdateShowFile_server(fileinfo.file_id).Wait();
477 | }
478 | Dispatcher.Invoke(() =>
479 | {
480 | serverFileDirectory.Text = Path.Combine(_currentDirectory_server.Select(o => o.name).ToArray());
481 | });
482 | }
483 | void LoadingShow() { Dispatcher.Invoke(() => { Loading.Visibility = Visibility.Visible; }); }
484 | void LoadingHide() { Dispatcher.Invoke(() => { Loading.Visibility = Visibility.Hidden; }); }
485 | private void Button_Click_Refresh(object sender, RoutedEventArgs e)
486 | {
487 | LoadingShow();
488 | Task.Run(() =>
489 | {
490 | ShowUserinfoAsync().Wait();
491 | AddDirectory_server(null).Wait();
492 | LoadingHide();
493 | });
494 | }
495 | private void Button_Click_Server_RootDirectory(object sender, RoutedEventArgs e)
496 | {
497 | LoadingShow();
498 | Task.Run(() =>
499 | {
500 | SubDirectory_server(true).Wait();
501 | LoadingHide();
502 | });
503 | }
504 | private void Button_Click_Server_SubDirectory(object sender, RoutedEventArgs e)
505 | {
506 | LoadingShow();
507 | Task.Run(() =>
508 | {
509 | SubDirectory_server().Wait();
510 | LoadingHide();
511 | });
512 |
513 | }
514 | #region 命名编辑
515 | private TaskType edit_type;
516 | private string edit_file_id;
517 | private void Button_Click_Server_CreatDirctory(object sender, RoutedEventArgs e)
518 | {
519 | edit_type = TaskType.目录;
520 | serverEditV.Visibility = Visibility.Visible;
521 | }
522 | private void Button_Click_serverEdit_OK(object sender, RoutedEventArgs e)
523 | {
524 | string name = serverEdit.Text;
525 | if (name == "")
526 | {
527 | serverEdit_tip.Text = "请输入名称";
528 | return;
529 | }
530 | if (TaskOK != null)
531 | {
532 | TaskOK(name);
533 | return;
534 | }
535 | if (name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
536 | {
537 | serverEdit_tip.Text = "含有非法字符 \\ / : * ? \" < > | 等";
538 | return;
539 | }
540 |
541 | if (edit_type == TaskType.目录)
542 | {
543 | //添加目录任务
544 | TaskMange.Add(new TaskInfo { Type = TaskType.目录, Name = name, parent_file_id = serverDirectory_file_id });
545 | }
546 | else if (edit_type == TaskType.改名)
547 | {
548 | //添加改名任务
549 | TaskMange.Add(new TaskInfo { Type = TaskType.改名, Name = name, file_id = edit_file_id, parent_file_id = serverDirectory_file_id });
550 | }
551 | Button_Click_serverEdit_Canel(sender, e);
552 | }
553 | Action TaskOK;
554 | private void Button_Click_Server_Search(object sender, RoutedEventArgs e)
555 | {
556 | if (serverFile.SelectedItem != null) serverEdit.Text = ((info_file)serverFile.SelectedItem).name;
557 |
558 | serverEditV.Visibility = Visibility.Visible;
559 | TaskOK = async (name) =>
560 | {
561 | Dispatcher.Invoke(() =>
562 | {
563 | LoadingShow();
564 | });
565 | var f = new file();
566 | f.parent_file_id = serverDirectory_file_id;
567 | f.drive_id = token.Instance.default_drive_id;
568 | f.query = $"parent_file_id='{serverDirectory_file_id}' and name match '{name}'";
569 | try
570 | {
571 | var r = await f.search();
572 | Dispatcher.Invoke(() =>
573 | {
574 | serverFile.ItemsSource = r.success ? r.obj.items : null;
575 | ShowServerTip(r.Error);
576 | });
577 |
578 | }
579 | catch (Exception ex)
580 | {
581 | Dispatcher.Invoke(() =>
582 | {
583 | ShowServerTip(ex.Message);
584 | });
585 | }
586 | TaskOK = null;
587 | Dispatcher.Invoke(() =>
588 | {
589 | Button_Click_serverEdit_Canel(sender, e);
590 | LoadingHide();
591 | });
592 | };
593 | }
594 | private void Button_Click_Server_Rename(object sender, RoutedEventArgs e)
595 | {
596 | if (serverFile.SelectedItem == null) return;
597 | var f = (info_file)serverFile.SelectedItem;
598 |
599 | serverEdit.Text = f.name;
600 | edit_type = TaskType.改名;
601 | edit_file_id = f.file_id;
602 | serverEditV.Visibility = Visibility.Visible;
603 | }
604 | private void Button_Click_serverEdit_Canel(object sender, RoutedEventArgs e)
605 | {
606 | serverEditV.Visibility = Visibility.Hidden;
607 | serverEdit_tip.Text = "";
608 | }
609 |
610 | #endregion
611 | private void Button_Click_Server_Del(object sender, RoutedEventArgs e)
612 | {
613 | foreach (info_file item in serverFile.SelectedItems)
614 | {
615 | Console.WriteLine($"Button_Click_Server_Del:{item.name},{item.file_id}");
616 | TaskMange.Add(new TaskInfo { Type = TaskType.删除, parent_file_id = serverDirectory_file_id, file_id = item.file_id, Name = item.name });
617 | }
618 | }
619 | private void serverFile_MouseDoubleClick(object sender, MouseButtonEventArgs e)
620 | {
621 | if (serverFile.SelectedItem == null) return;
622 | var f = (info_file)serverFile.SelectedItem;
623 | Console.WriteLine($"serverFile_MouseDoubleClick:{f.name}");
624 | if (f.type == "folder")
625 | {
626 | Console.WriteLine($"点击目录:{f.name}:{fullDirectory}");
627 | LoadingShow();
628 | Task.Run(() =>
629 | {
630 | AddDirectory_server(f).Wait();
631 | LoadingHide();
632 | });
633 | }
634 | }
635 |
636 | private void Button_Click_Server_QrShow(object sender, RoutedEventArgs e)
637 | {
638 | if (serverFile.SelectedItem == null) return;
639 | var f = (info_file)serverFile.SelectedItem;
640 | if (f.type != "folder")
641 | {
642 | serverQRV.Visibility = Visibility.Visible;
643 | serverQR.Source = new BitmapImage(new Uri(Util.GetQrUrl(f.download_url)));
644 | }
645 | }
646 |
647 | private void serverQR_Image_MouseMove(object sender, MouseEventArgs e)
648 | {
649 | serverQRV.Visibility = Visibility.Hidden;
650 | }
651 |
652 | #endregion
653 | #region 任务区
654 | private void Button_Click_TaskClear(object sender, RoutedEventArgs e)
655 | {
656 | TaskMange.Clear();
657 | }
658 | private void Button_Click_TaskSync(object sender, RoutedEventArgs e)
659 | {
660 | new SyncFile().ShowDialog();
661 | SyncButton.Background = AsyncTaskMange.Instance.Status ? System.Windows.Media.Brushes.Orange : null;
662 | }
663 | private void Button_down_Click(object sender, RoutedEventArgs e)
664 | {
665 | new RefererDownload.MainWindow().Show();
666 | }
667 | private void task_MaxCount_SelectionChanged(object sender, SelectionChangedEventArgs e)
668 | {
669 | TaskMange.MaxCount = (int)task_MaxCount.SelectedItem;
670 | }
671 | private void Button_Click_TaskCopyContent(object sender, RoutedEventArgs e)
672 | {
673 | List del = new List();
674 | foreach (TaskInfo item in taskList.SelectedItems)
675 | {
676 | //复制任务名字
677 | Clipboard.SetDataObject(item.Name);
678 | //Clipboard.SetText(item.Name);
679 | }
680 | }
681 | private void Button_Click_TaskDel(object sender, RoutedEventArgs e)
682 | {
683 | List del = new List();
684 | foreach (TaskInfo item in taskList.SelectedItems)
685 | {
686 | //删除任务
687 | del.Add(item);
688 | }
689 | foreach (var item in del)
690 | {
691 | TaskMange.Remove(item);
692 | }
693 | }
694 |
695 | #endregion
696 | #region 顶部提示
697 | Action topTip_okRun;
698 | private void Button_Click_topTip_OK(object sender, RoutedEventArgs e)
699 | {
700 | topTip_okRun?.Invoke();
701 | Button_Click_topTip_Canel(sender, e);
702 | }
703 | private void Button_Click_topTip_Canel(object sender, RoutedEventArgs e)
704 | {
705 | topTip.Visibility = Visibility.Hidden;
706 | topTip_okRun = null;
707 | }
708 | void ShowTopTip(string str, Action ok)
709 | {
710 | Dispatcher.Invoke(() =>
711 | {
712 | topTip.Visibility = Visibility.Visible;
713 | topTip_tip.Text = str;
714 | topTip_okRun = ok;
715 | });
716 | }
717 | #endregion
718 | #region 监视剪贴板
719 | private ClipboardWatcher ClipboardWatcher;
720 | void InitClipboard()
721 | {
722 | ClipboardWatcher = new ClipboardWatcher(this);
723 | ClipboardWatcher.DrawClipboard += ClipboardWatcher_DrawClipboard;
724 | ClipboardWatcher.Start();
725 | }
726 | private void ClipboardWatcher_DrawClipboard(object sender, EventArgs e)
727 | {
728 | try
729 | {
730 | ClipboardChange();
731 | }
732 | catch (Exception) { }
733 | }
734 | void ClipboardChange()
735 | {
736 | if (Clipboard.ContainsText())
737 | {
738 | var str = Clipboard.GetText();
739 | if (str.Contains("fap://"))
740 | {
741 | var r = Regex.Match(str, "fap://([a-fA-F0-9]+)");
742 | if (r.Success)
743 | {
744 | var fap = Fap.FAP_get(r.Groups[1].Value);
745 | if (fap != null)
746 | {
747 | str = "";
748 | foreach (var item in fap.list)
749 | {
750 | str += Environment.NewLine + item.name;
751 | }
752 | ShowTopTip("要尝试秒传文件吗?" + Environment.NewLine + str, () =>
753 | {
754 | Fap.FAP_rapid_upload(fap, serverDirectory_file_id, cb => { Dispatcher.Invoke(cb); });
755 | });
756 | }
757 | }
758 | }
759 | }
760 | if (Clipboard.ContainsFileDropList())
761 | {
762 | var files = Clipboard.GetFileDropList();
763 | var str = "";
764 | foreach (var item in files)
765 | {
766 | str += Environment.NewLine + item;
767 | }
768 |
769 | ShowTopTip("要上传剪贴板中的文件吗?" + str, () =>
770 | {
771 | foreach (string item in files)
772 | {
773 | //添加上传任务
774 | var f = new FileInfo(item);
775 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, FullName = f.FullName, Name = f.Name, parent_file_id = serverDirectory_file_id });
776 | }
777 | });
778 |
779 | }
780 | }
781 | #endregion
782 | #region 拖拽上传文件
783 | private void serverFile_DragEnter(object sender, DragEventArgs e)
784 | {
785 | if (e.Data.GetDataPresent(DataFormats.FileDrop))
786 | {
787 | e.Effects = DragDropEffects.Link;
788 | List files = new List();
789 | foreach (string item in (System.Array)e.Data.GetData(DataFormats.FileDrop))
790 | {
791 | files.Add(new FileInfo(item).Name);
792 | }
793 | localFileTip.Text = "鼠标放下上传文件:\n" + string.Join(",", files);
794 | localFileTip.Visibility = Visibility.Visible;
795 | }
796 | else
797 | {
798 | e.Effects = DragDropEffects.None;
799 | }
800 | }
801 | private void serverFile_DragLeave(object sender, DragEventArgs e)
802 | {
803 | localFileTip.Visibility = Visibility.Hidden;
804 | }
805 | private void serverFile_Drop(object sender, DragEventArgs e)
806 | {
807 | localFileTip.Visibility = Visibility.Hidden;
808 | foreach (string item in (System.Array)e.Data.GetData(DataFormats.FileDrop))
809 | {
810 | //添加上传任务
811 | var f = new FileInfo(item);
812 | TaskMange.Add(new TaskInfo { Type = TaskType.上传, FullName = f.FullName, Name = f.Name, parent_file_id = serverDirectory_file_id });
813 | }
814 | }
815 |
816 | #endregion 拖拽上传文件
817 | #region 点击链接
818 | bool _LoginOK = true;
819 | bool LoginOK
820 | {
821 | set
822 | {
823 | if (_LoginOK == value) return;
824 | _LoginOK = value;
825 | Dispatcher.Invoke(() => { ClickLogin.Text = _LoginOK ? "退出登录" : "点击登录"; });
826 | }
827 | get
828 | {
829 | return _LoginOK;
830 | }
831 | }
832 | private void Click_Link_LoginOut(object sender, RoutedEventArgs e)
833 | {
834 | if (!LoginOK)
835 | {
836 | new Login().ShowDialog();
837 | RefreshData();
838 | return;
839 | }
840 | try
841 | {
842 | token.Instance.refresh_token = "";
843 | token.Instance.access_token = "";
844 | token.Instance.Config_Save();
845 | AsyncTaskMange.Instance.Clear();
846 | Task.Run(ShowUserinfoAsync);
847 | }
848 | catch (Exception ex)
849 | {
850 | MessageBox.Show(ex.Message);
851 | }
852 | }
853 | private void Click_Link_Code(object sender, RoutedEventArgs e)
854 | {
855 | try
856 | {
857 | Process.Start("https://github.com/PlainWizard/aliyundrive-Client-CSharp");
858 | }
859 | catch (Exception ex)
860 | {
861 | MessageBox.Show(ex.Message);
862 | }
863 | }
864 | ///
865 | /// 支持开源
866 | /// 开发交流群:476304388
867 | ///
868 | ///
869 | ///
870 | private void Click_Link_Supporting(object sender, RoutedEventArgs e)
871 | {
872 | try
873 | {
874 | Process.Start("http://6op.cn/PlainWizard.aliyundrive");
875 | }
876 | catch (Exception ex)
877 | {
878 | MessageBox.Show(ex.Message);
879 | }
880 | }
881 |
882 | #endregion
883 |
884 | }
885 |
886 | }
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // 有关程序集的一般信息由以下
8 | // 控制。更改这些特性值可修改
9 | // 与程序集关联的信息。
10 | [assembly: AssemblyTitle("aliyundrive-Client-CSharp")]
11 | [assembly: AssemblyDescription("阿里云盘客户端 https://github.com/PlainWizard/aliyundrive-Client-CSharp")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("PlainWizard")]
14 | [assembly: AssemblyProduct("aliyundrive-Client-CSharp")]
15 | [assembly: AssemblyCopyright("Copyright © 2021")]
16 | [assembly: AssemblyTrademark("PlainWizard")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // 将 ComVisible 设置为 false 会使此程序集中的类型
20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
21 | //请将此类型的 ComVisible 特性设置为 true。
22 | [assembly: ComVisible(false)]
23 |
24 | //若要开始生成可本地化的应用程序,请设置
25 | //.csproj 文件中的 CultureYouAreCodingWith
26 | //例如,如果您在源文件中使用的是美国英语,
27 | //使用的是美国英语,请将 设置为 en-US。 然后取消
28 | //对以下 NeutralResourceLanguage 特性的注释。 更新
29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置
36 | //(未在页面中找到资源时使用,
37 | //或应用程序资源字典中找到时使用)
38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
39 | //(未在页面中找到资源时使用,
40 | //、应用程序或任何主题专用资源字典中找到时使用)
41 | )]
42 |
43 |
44 | // 程序集的版本信息由下列四个值组成:
45 | //
46 | // 主版本
47 | // 次版本
48 | // 生成号
49 | // 修订号
50 | //
51 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
52 | //通过使用 "*",如下所示:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.1")]
55 | [assembly: AssemblyFileVersion("1.0.0.1")]
56 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本: 4.0.30319.42000
5 | //
6 | // 对此文件的更改可能导致不正确的行为,如果
7 | // 重新生成代码,则所做更改将丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace aliyundrive_Client_CSharp.Properties
12 | {
13 |
14 |
15 | ///
16 | /// 强类型资源类,用于查找本地化字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// 返回此类使用的缓存 ResourceManager 实例。
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("aliyundrive_Client_CSharp.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// 覆盖当前线程的 CurrentUICulture 属性
56 | /// 使用此强类型的资源类的资源查找。
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace aliyundrive_Client_CSharp.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # aliyundrive-Client-CSharp
2 | C# Client for aliyundrive
3 |
4 | 下载器直接用的 [RefererDownload](https://github.com/PlainWizard/RefererDownload),需要一起下载才能编译
5 |
6 | ## 概述
7 |
8 | 学了一点WPF皮毛,刚好阿里云盘在公测,所以写了个c#同步文件到网盘的客户端
9 |
10 | 主要用于上传文件,文件查看方面不完善,建议使用官方app智能分类管理
11 |
12 | 阿里云盘Windows客户端
13 |
14 | 部分功能:
15 |
16 | - `自动同步上传`
17 | - `文件批量上传`
18 | - `批量拖拽上传`
19 | - `剪贴板上传`
20 | - `文件目录增删改查`
21 | - `复制下载链接`
22 | - `生成链接二维码`
23 | - `防盗链下载器`
24 | - `文件在线分享`
25 | - `文件夹分享`
26 |
27 | ## 运行环境
28 | - .NET Framework 4.6
29 |
30 | ## 成品
31 |
32 | 编译好成品放在云盘欢迎下载体验云盘下载
33 |
34 | 把文件放在哪个目录里运行,就是以哪个目录为根目录进行同步
35 |
36 | - [下载客户端体验](https://pan.wyfxw.cn/plainwizard/aliyundrive-Client-CSharp.exe)
37 |
38 | 交流反馈群:476304388
39 |
40 | 软件由个人开发,如果觉得对您有帮助,并愿意赞赏:[支付宝收款码](https://pan.wyfxw.cn/plainwizard/ali.jpg)
41 |
42 | 有你的支持,是我前进的动力!
43 |
44 | ## 相关链接
45 |
46 | - [防盗链下载器17KB](https://github.com/PlainWizard/RefererDownload)
47 |
48 | - [FAP文件在线分享](https://file.wyfxw.cn/)
49 |
50 | - [阿里云盘公测地址](https://www.aliyundrive.com/apply)
51 |
52 | 将阿里云多年企业服务的技术积累与沉淀,服务于个人用户,携手用户进入更快、更安全、更流畅、更智能的个人云时代。
--------------------------------------------------------------------------------
/SyncFile.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 排除的路径,如Logs
16 | 一行一个,模糊匹配
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/SyncFile.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using System.Windows.Controls;
10 | using System.Windows.Data;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Imaging;
15 | using System.Windows.Shapes;
16 | using System.Windows.Threading;
17 |
18 | namespace aliyundrive_Client_CSharp
19 | {
20 | ///
21 | /// SyncFile.xaml 的交互逻辑
22 | ///
23 | public partial class SyncFile : Window
24 | {
25 | public SyncFile()
26 | {
27 | InitializeComponent();
28 | WinMethod.DisableMinmizebox(this);
29 | taskList.ItemsSource = TaskMange.Tasks;
30 | exclude.Text = string.Join(Environment.NewLine, AsyncTaskMange.Instance.ignore);
31 | GetList();
32 | ChangeStatus();
33 | }
34 | void GetList()
35 | {
36 | var dir=new DirectoryInfo(MainWindow.localRootDir);
37 | foreach (var item in dir.GetFiles("*",SearchOption.AllDirectories).Select(x=>x.Extension).Distinct().ToList())
38 | {
39 | if(!AsyncTaskMange.Instance.isExtExists(item))
40 | AsyncTaskMange.Instance.ExtList.Add(new DataItem { Name = item });
41 | }
42 | AsyncTaskMange.Instance.Config_Save();
43 | checkListBox.ItemsSource = AsyncTaskMange.Instance.ExtList;
44 | }
45 | void Sync() {
46 | if (!AsyncTaskMange.Instance.Status) return;
47 | var dir = new DirectoryInfo(MainWindow.localRootDir);
48 | foreach (var item in dir.GetFiles("*", SearchOption.AllDirectories))
49 | {
50 | AsyncTaskMange.Instance.Add(item.FullName);
51 | }
52 | }
53 | void ChangeStatus()
54 | {
55 | if (AsyncTaskMange.Instance.Status)
56 | {
57 | Start.IsEnabled = false;
58 | Stop.IsEnabled = true;
59 | exclude.IsEnabled = false;
60 | checkListBox.IsEnabled = false;
61 | AsyncTaskMange.Instance.ignore= exclude.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Select(o=>o).ToList();
62 | }
63 | else
64 | {
65 | Start.IsEnabled = true;
66 | Stop.IsEnabled = false;
67 | exclude.IsEnabled = true;
68 | checkListBox.IsEnabled = true;
69 | }
70 | AsyncTaskMange.Instance.Config_Save();
71 | }
72 | private void Button_Click_Status(object sender, RoutedEventArgs e)
73 | {
74 | try
75 | {
76 | AsyncTaskMange.Instance.Status = !AsyncTaskMange.Instance.Status;
77 | ChangeStatus();
78 | if (AsyncTaskMange.Instance.Status) Sync();
79 | }
80 | catch (Exception ex)
81 | {
82 | MessageBox.Show(ex.Message);
83 | }
84 | }
85 |
86 | private void Button_Click_TaskDel(object sender, RoutedEventArgs e)
87 | {
88 | List del = new List();
89 | foreach (TaskInfo item in taskList.SelectedItems)
90 | {
91 | //删除任务
92 | del.Add(item);
93 | }
94 | foreach (var item in del)
95 | {
96 | TaskMange.Remove(item);
97 | }
98 | }
99 | }
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/TaskInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Runtime.CompilerServices;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace aliyundrive_Client_CSharp
11 | {
12 |
13 | public enum TaskType
14 | {
15 | 删除, 上传, 秒传, 目录, 改名,同步, 分享
16 | }
17 |
18 |
19 | public class TaskInfo : INotifyPropertyChanged
20 | {
21 | public string parent_file_id = "root";
22 | public string file_id = "";
23 | public List file_id_list;
24 | public string sha1 = "";
25 | public long size = 0;
26 | public string file_name = "";
27 | public string FullName = "";
28 | private TaskType _type;
29 | private string _name;
30 | private int _step;
31 | private int _status = 0;
32 | public Task task;
33 | private CancellationTokenSource cancelTokenSource=new CancellationTokenSource();
34 | public CancellationToken cancelToken { get { return cancelTokenSource.Token; } }
35 | public void Cancel() { cancelTokenSource.Cancel(); }
36 | public long Position;
37 | public long Length;
38 | public string Foreground
39 | {
40 | get
41 | {
42 | if (_status == 1) return "#f8b800";
43 | if (_status == 2) return "#39f";
44 | return "#f60";
45 | }
46 | }
47 |
48 | public int Status//0 初始,1正在执行,2成功,3失败
49 | {
50 | set
51 | {
52 | _status = value;
53 | if (_status > 1) Step = 100;
54 | OnPropertyChanged("Foreground");
55 | TaskMange.StatusChange(this);
56 | }
57 | get
58 | {
59 | return _status;
60 | }
61 | }
62 | public TaskType Type
63 | {
64 | set
65 | {
66 | UpdateProperty(ref _type, value);
67 | }
68 | get
69 | {
70 | return _type;
71 | }
72 | }
73 | public string Name
74 | {
75 | set
76 | {
77 | UpdateProperty(ref _name, value);
78 | }
79 | get
80 | {
81 | return _name;
82 | }
83 | }
84 | public virtual int Step
85 | {
86 | set
87 | {
88 | UpdateProperty(ref _step, value);
89 | }
90 | get
91 | {
92 | return _step;
93 | }
94 | }
95 | private void UpdateProperty(ref T properValue, T newValue, [CallerMemberName] string propertyName = "")
96 | {
97 | if (object.Equals(properValue, newValue))
98 | {
99 | return;
100 | }
101 | properValue = newValue;
102 |
103 | OnPropertyChanged(propertyName);
104 | }
105 | public event PropertyChangedEventHandler PropertyChanged;
106 | protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
107 | {
108 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
109 | }
110 |
111 |
112 | }
113 |
114 |
115 | public class DataItem : INotifyPropertyChanged
116 | {
117 | private string name;
118 | private bool isEnabled;
119 |
120 | public string Name
121 | {
122 | get { return name; }
123 | set
124 | {
125 | name = value;
126 | OnPropertyChanged("Name");
127 | }
128 | }
129 |
130 | public bool IsEnabled
131 | {
132 | get { return isEnabled; }
133 | set
134 | {
135 | isEnabled = value;
136 | OnPropertyChanged("IsEnabled");
137 | }
138 | }
139 |
140 | #region INotifyPropertyChanged Members
141 |
142 | public event PropertyChangedEventHandler PropertyChanged;
143 |
144 | #endregion
145 |
146 | protected virtual void OnPropertyChanged(string propertyName)
147 | {
148 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
149 | }
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/TaskMange.cs:
--------------------------------------------------------------------------------
1 | using aliyundrive_Client_CSharp.aliyundrive;
2 | using System;
3 | using System.Collections.Concurrent;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.ComponentModel;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Text.RegularExpressions;
10 | using System.Threading.Tasks;
11 | using Newtonsoft.Json;
12 | using System.Windows;
13 | using System.Threading;
14 |
15 | namespace aliyundrive_Client_CSharp
16 | {
17 | public class TaskMange
18 | {
19 | private static ObservableCollection tasks = new ObservableCollection();
20 | public static ObservableCollection Tasks { get { return tasks; } }
21 | protected static object lock_obj = new object();
22 | public static int MaxCount { get; set; }
23 | static int CurrentCount { get; set; }
24 | public static Action OnTaskChange;
25 | public static Action OnServerFileChange;
26 | public static void StatusChange(TaskInfo task)
27 | {
28 | if (task.Status == 1) CurrentCount++;
29 | else if (task.Status > 1) CurrentCount--;
30 | if (CurrentCount < MaxCount)
31 | {
32 | lock (lock_obj)
33 | {
34 | foreach (var item in tasks)
35 | {
36 | if (CurrentCount == MaxCount) break;
37 | if (item.Status == 0) item.task = RunTask(item);
38 | }
39 | }
40 | }
41 | OnTaskChange();
42 | }
43 | public static string GetHeadStr()
44 | {
45 | var list = tasks.ToList();
46 | //0 初始,1正在执行,2成功,3失败
47 | int allcount = list.Count
48 | , allcount0 = list.Where(t => { return t.Status == 0; }).Count()
49 | , allcount1 = list.Where(t => { return t.Status == 1; }).Count()
50 | , allcount2 = list.Where(t => { return t.Status == 2; }).Count()
51 | , allcount3 = list.Where(t => { return t.Status == 3; }).Count();
52 | string headstr = $"任务{allcount}({allcount0}/{allcount1}/{allcount2}/{allcount3})";
53 | return headstr;
54 | }
55 | static Task RunTask(TaskInfo task)
56 | {
57 | task.Status = 1;
58 | return Task.Run(async () =>
59 | {
60 | switch (task.Type)
61 | {
62 | case TaskType.上传: await Task_ServerUploadAsync(task); break;
63 | case TaskType.同步: await Task_ServerUploadAsync(task); break;
64 | case TaskType.删除: await Task_ServerDelFileAsync(task); break;
65 | case TaskType.目录: await Task_Server_folder(task); break;
66 | case TaskType.改名: await Task_Server_rename(task); break;
67 | case TaskType.分享: await Task_Server_share(task); break;
68 | }
69 | });
70 | }
71 | public static void Add(TaskInfo task)
72 | {
73 | lock (lock_obj)
74 | {
75 | tasks.Insert(0, task);
76 | if (task.Status != 0) return;
77 | if (CurrentCount < MaxCount) task.task = RunTask(task);
78 | }
79 | OnTaskChange();
80 | }
81 | public static void Clear()
82 | {
83 | lock (lock_obj)
84 | {
85 | foreach (var item in tasks)
86 | {
87 | item.Cancel();
88 | }
89 | tasks.Clear();
90 | }
91 | OnTaskChange();
92 | }
93 | public static void Remove(TaskInfo task)
94 | {
95 | lock (lock_obj)
96 | {
97 | task.Cancel();
98 | tasks.Remove(task);
99 | }
100 | OnTaskChange();
101 | }
102 | static async Task Task_Server_folder(TaskInfo task)
103 | {
104 | try
105 | {
106 | var r = await new upload().folder(task.parent_file_id, task.Name, task);
107 | if (r.success)
108 | {
109 | if (r.obj.exist) throw new Exception("目录已存在");
110 | OnServerFileChange(task.parent_file_id);
111 | task.Status = 2;
112 | }
113 | else
114 | {
115 | task.Status = 3;
116 | task.Name = task.Name + $"[{r.Error}]";
117 | }
118 | }
119 | catch (Exception ex)
120 | {
121 | task.Status = 3;
122 | task.Name = task.Name + $"[{ex.Error()}]";
123 | }
124 | }
125 | static async Task Task_Server_rename(TaskInfo task)
126 | {
127 | try
128 | {
129 | var r = await new upload().rename(task.file_id, task.Name, task);
130 | if (r.success)
131 | {
132 | OnServerFileChange(task.parent_file_id);
133 | task.Status = 2;
134 | }
135 | else
136 | {
137 | task.Status = 3;
138 | task.Name = task.Name + $"[{r.Error}]";
139 | }
140 | }
141 | catch (Exception ex)
142 | {
143 | task.Status = 3;
144 | task.Name = task.Name + $"[{ex.Error()}]";
145 | }
146 | }
147 | static async Task Task_Server_share(TaskInfo task)
148 | {
149 | try
150 | {
151 | var r = await new upload().share(task.file_id_list, task.Name, task);
152 | if (r.success)
153 | {
154 | var content = JsonConvert.DeserializeObject>(r.body);
155 | var password = content["share_pwd"];
156 | var share_id = content["share_id"];
157 | var share_msg = content["share_msg"];
158 | var share_url = content["share_url"];
159 | // Clipboard.SetText($"{share_msg} {share_url}");
160 | task.Name = task.Name + $"{share_msg} {share_url}";
161 | task.Status = 2;
162 | }
163 | else
164 | {
165 | task.Status = 3;
166 | task.Name = task.Name + $"[{r.Error}]";
167 | }
168 | }
169 | catch (Exception ex)
170 | {
171 | task.Status = 3;
172 | task.Name = task.Name + $"[{ex.Error()}]";
173 | }
174 | }
175 | static async Task Task_ServerDelFileAsync(TaskInfo task)
176 | {
177 | try
178 | {
179 | var r = await new batch().del(task.file_id);
180 | if (r.success)
181 | {
182 | OnServerFileChange(task.parent_file_id);
183 | task.Status = 2;
184 | }
185 | else
186 | {
187 | task.Status = 3;
188 | task.Name = task.Name + $"[{r.Error}]";
189 | }
190 | }
191 | catch (Exception ex)
192 | {
193 | task.Status = 3;
194 | task.Name = task.Name + $"[{ex.Error()}]";
195 | }
196 | }
197 |
198 | static async Task Task_ServerUploadAsync(TaskInfo task)
199 | {
200 | try
201 | {
202 | var r = await new upload().Upload(task.parent_file_id, task.Name, task);
203 | if (r.success)
204 | {
205 | if (task.Type != TaskType.同步)
206 | {
207 | if (r.obj.rapid_upload) task.Type = TaskType.秒传;
208 | OnServerFileChange(task.parent_file_id);
209 | }
210 | task.Status = 2;
211 | }
212 | else
213 | {
214 | task.Status = 3;
215 | task.Name = task.Name + $"[{r.Error}]";
216 | }
217 | }
218 | catch (Exception ex)
219 | {
220 | task.Status = 3;
221 | task.Name = task.Name + $"[{ex.Error()}]";
222 | }
223 |
224 | }
225 | }
226 |
227 | public class AsyncTaskMange : Config
228 | {
229 | private ObservableCollection extList = new ObservableCollection();
230 | public ObservableCollection ExtList { get { return extList; } }
231 | [NonSerialized]
232 | public Action invoke;
233 | ConcurrentQueue queue = new ConcurrentQueue();
234 | [NonSerialized]
235 | public Dictionary ParentIDs = new Dictionary();
236 | public Dictionary serverFileCache = new Dictionary();
237 | public void Clear()
238 | {
239 | ParentIDs = new Dictionary();
240 | serverFileCache = new Dictionary();
241 | queue = new ConcurrentQueue();
242 | Config_Save();
243 | }
244 | public bool isExtExists(string name)
245 | {
246 | return ExtList.Where(o => o.Name == name).Count() > 0;
247 | }
248 | public bool Status = false;
249 | public List ignore = new List() { "\\node_modules", "\\logs", "\\.vs", "\\.git", "\\obj", "\\bin", "\\packages" };
250 | bool adding = false;
251 | public async void Add(string file)
252 | {
253 | if (!Status) return;
254 | await Task.Delay(6000);//优化同步速度O(∩_∩)O
255 |
256 | lock (lock_obj)
257 | {
258 | if (queue.Where(s => s == file).Count() > 0) return;
259 | queue.Enqueue(file);
260 | if (adding) return;
261 | adding = true;
262 | }
263 | AddQueue();
264 | }
265 | public async void AddQueue()
266 | {
267 | string file;
268 | Console.WriteLine($"队列数量:{queue.Count}");
269 | if (!queue.TryDequeue(out file))
270 | {
271 | adding = false;
272 | return;
273 | }
274 | var f = new System.IO.FileInfo(file);
275 | try
276 | {
277 | if (!f.Exists) return;
278 |
279 | foreach (var item in ignore)
280 | {
281 | if (file.Contains(item)) return;
282 | }
283 | var ext = ExtList.Where(o => o.IsEnabled && o.Name == f.Extension).ToList();
284 | if (ext.Count == 0) return;
285 | if (!f.FullName.StartsWith(MainWindow.localRootDir)) throw new Exception("根目录异常");
286 | var task = new TaskInfo { Type = TaskType.同步, FullName = f.FullName, Name = f.Name };
287 | using (var stream = Util.GetFileStream(f.FullName))
288 | {
289 | task.sha1 = Util.sha1(stream);
290 | task.size = stream.Length;
291 | }
292 | if (serverFileCache.ContainsKey(file) && serverFileCache[file] == $"{task.sha1};{task.size}") return;
293 |
294 | string fid = "root";
295 | if (f.Directory.FullName != MainWindow.localRootDir)
296 | {
297 | var relativeFile = f.Directory.FullName.Substring(MainWindow.localRootDir.Length + 1).Replace("\\", "/") + "/";
298 | //dir 所在相对当前路径的目录 格式为 一层目录(aaa/) 二层目录(aaa/bbb/)
299 | if (!ParentIDs.ContainsKey(relativeFile))
300 | {
301 | var ms = Regex.Matches(relativeFile, "([^/]+?)/");
302 | var u = new upload();
303 | foreach (Match m in ms)
304 | {
305 | Console.WriteLine($"同步创建目录[{relativeFile}]:{m.Groups[1].Value}");
306 | fid = await u.getfolder(fid, m.Groups[1].Value);
307 | }
308 | ParentIDs[relativeFile] = fid;
309 | }
310 | fid = ParentIDs[relativeFile];
311 | }
312 | task.parent_file_id = fid;
313 | var r = await new file().search(fid, f.Name);
314 | if (!r.Yes) throw new Exception(r.Error);
315 | if (r.obj.items.Count > 0)
316 | {
317 | foreach (var item in r.obj.items)
318 | {
319 | serverFileCache[file] = $"{task.sha1};{task.size}";
320 | Config_Save();
321 | if (item.content_hash == task.sha1 && item.size == task.size) return;
322 | }
323 | }
324 |
325 | invoke(() =>
326 | {
327 | TaskMange.Add(task);
328 | });
329 | }
330 | catch (Exception ex)
331 | {
332 | invoke(() =>
333 | {
334 | TaskMange.Add(new TaskInfo { Type = TaskType.同步, FullName = f.FullName, Status = 3, Name = f.Name + $"[{ex.Error()}]" });
335 | });
336 | }
337 | finally
338 | {
339 | _ = Task.Run(AddQueue);
340 | }
341 | }
342 | }
343 |
344 | }
345 |
--------------------------------------------------------------------------------
/WinAPI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Interop;
9 | using System.Windows.Threading;
10 |
11 | namespace aliyundrive_Client_CSharp
12 | {
13 | class WinAPI
14 | {
15 | [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
16 | public static extern int SetWindowLong(IntPtr hMenu, int nIndex, int dwNewLong);
17 |
18 | }
19 | class WinMethod
20 | {
21 | public static void DisableMinmizebox(Window window)
22 | {
23 | Task.Run(()=> {
24 | window.Dispatcher.Invoke(() => {
25 | WinAPI.SetWindowLong(new WindowInteropHelper(window).Handle, -16, 0x16CD0000);//设定一个新的窗口风格
26 | });
27 | });
28 | }
29 | }
30 |
31 | class ClipboardWatcher
32 | {
33 | [DllImport("user32.dll", SetLastError = true)]
34 | private extern static void AddClipboardFormatListener(IntPtr hwnd);
35 |
36 | [DllImport("user32.dll", SetLastError = true)]
37 | private extern static void RemoveClipboardFormatListener(IntPtr hwnd);
38 |
39 | private const int WM_CLIPBOARDUPDATE = 0x031D;
40 | private IntPtr handle;
41 | private HwndSource hwndSource = null;
42 | public event EventHandler DrawClipboard;
43 | private void RaiseClipboardUpdata()
44 | {
45 | DrawClipboard?.Invoke(this, EventArgs.Empty);
46 | }
47 |
48 | private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
49 | {
50 | if (msg == WM_CLIPBOARDUPDATE)
51 | {
52 | this.RaiseClipboardUpdata();
53 | handled = true;
54 | }
55 | return IntPtr.Zero;
56 | }
57 | public ClipboardWatcher(Window window)
58 | {
59 | handle = new System.Windows.Interop.WindowInteropHelper(window).Handle;
60 | hwndSource = HwndSource.FromHwnd(handle);
61 | hwndSource.AddHook(WndProc);
62 | }
63 | ~ClipboardWatcher(){
64 | Stop();
65 | }
66 | public void Start()
67 | {
68 | AddClipboardFormatListener(handle);
69 | }
70 | public void Stop()
71 | {
72 | RemoveClipboardFormatListener(handle);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/alipay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlainWizard/aliyundrive-Client-CSharp/71dc852a2839aa08ea82d909ea2b77de962e4eef/alipay.jpg
--------------------------------------------------------------------------------
/aliyundrive-Client-CSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}
9 | WinExe
10 | aliyundrive_Client_CSharp
11 | aliyundrive-Client-CSharp
12 | v4.6
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 | true
18 |
19 |
20 |
21 |
22 | AnyCPU
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | AnyCPU
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 | favicon.ico
42 |
43 |
44 |
45 | packages\Costura.Fody.4.1.0\lib\net40\Costura.dll
46 |
47 |
48 | packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 4.0
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | MSBuild:Compile
68 | Designer
69 |
70 |
71 |
72 | SyncFile.xaml
73 |
74 |
75 |
76 |
77 |
78 | Designer
79 | MSBuild:Compile
80 |
81 |
82 | MSBuild:Compile
83 | Designer
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | App.xaml
93 | Code
94 |
95 |
96 |
97 |
98 | Login.xaml
99 |
100 |
101 | MainWindow.xaml
102 | Code
103 |
104 |
105 | Designer
106 | MSBuild:Compile
107 |
108 |
109 |
110 |
111 | Code
112 |
113 |
114 | True
115 | True
116 | Resources.resx
117 |
118 |
119 | True
120 | Settings.settings
121 | True
122 |
123 |
124 | ResXFileCodeGenerator
125 | Resources.Designer.cs
126 |
127 |
128 |
129 |
130 | SettingsSingleFileGenerator
131 | Settings.Designer.cs
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | {686090c2-35d2-44bc-a1b7-f219a183d404}
146 | RefererDownload
147 |
148 |
149 |
150 |
151 |
152 |
153 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/aliyundrive-Client-CSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29728.190
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aliyundrive-Client-CSharp", "aliyundrive-Client-CSharp.csproj", "{397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CAE0E549-D2D8-4BC0-9987-9518C2397680}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefererDownload", "..\RefererDownload\RefererDownload.csproj", "{686090C2-35D2-44BC-A1B7-F219A183D404}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {397FCE2C-E807-4D2F-9B3F-DA3B7E2315B9}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {686090C2-35D2-44BC-A1B7-F219A183D404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {686090C2-35D2-44BC-A1B7-F219A183D404}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {686090C2-35D2-44BC-A1B7-F219A183D404}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {686090C2-35D2-44BC-A1B7-F219A183D404}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {228EF5B3-570F-48BD-9138-1B2B0D9A1C32}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/aliyundrive/Hclient.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Net.Http.Headers;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using System.Xml;
12 |
13 | namespace aliyundrive_Client_CSharp.aliyundrive
14 | {
15 | // HttpClient源码 https://github.com/dotnet/corefx/blob/d69d441dfb0710c2a34155c7c4745db357b14c96/src/System.Net.Http/src/System/Net/Http/HttpClient.cs
16 | class Hclient : HttpClient where T : MagBase
17 | {
18 | public Hclient() : base()//new HclientProcessing()
19 | {
20 | }
21 | public async Task> GetAsJsonAsync(string url)
22 | {
23 | var r = new HttpResult();
24 | try
25 | {
26 | var response = await GetAsync(url);
27 | r.success = response.IsSuccessStatusCode;
28 | r.body = await response.Content.ReadAsStringAsync();
29 | r.obj = JsonConvert.DeserializeObject(r.body);
30 | r.code = r.obj.code;
31 | r.message = r.obj.message;
32 | r.ok = true;
33 | }
34 | catch (Exception ex)
35 | {
36 | r.err = ex.Error();
37 | }
38 | return r;
39 | }
40 | public async Task> PostAsJsonAsync(string url, object data, TaskInfo task=null, bool redirect = false)
41 | {
42 | var c = new HclientContent(JsonConvert.SerializeObject(data), task);
43 | DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Instance.access_token);
44 | var r = new HttpResult();
45 | try
46 | {
47 | var response = await PostAsync(url, c);
48 | r.success = response.IsSuccessStatusCode;
49 | r.body = await response.Content.ReadAsStringAsync();
50 | r.obj = JsonConvert.DeserializeObject(r.body);
51 | r.code = r.obj.code;
52 | r.message = r.obj.message;
53 | r.ok = true;
54 | if (r.code == "AccessTokenInvalid")
55 | {
56 | Console.WriteLine($"AccessToken过期[{redirect}]");
57 | if (!redirect)
58 | {
59 | var re = await token.Instance.Refresh();
60 | if (re.Yes) return await PostAsJsonAsync(url, data, task, true);
61 | }
62 | }
63 | }
64 | catch (Exception ex)
65 | {
66 | r.err = ex.Error();
67 | }
68 | return r;
69 | }
70 | public async Task> PutAsJsonAsync(string url, Stream stream, TaskInfo task)
71 | {
72 | var r = new HttpResult();
73 | var c = new HclientContent(stream, task);
74 | try
75 | {
76 | var response = await PutAsync(url, c, task.cancelToken);
77 | r.success = response.IsSuccessStatusCode;
78 | r.ok = true;
79 | if (r.success) r.body = response.Headers.ETag.Tag;
80 | else
81 | {
82 | r.body = await response.Content.ReadAsStringAsync();
83 | var xml = new XmlDocument();
84 | xml.LoadXml(r.body);
85 | var err =
86 | r.code = xml.SelectSingleNode("/Error/Code").InnerText;
87 | r.message = xml.SelectSingleNode("/Error/Message").InnerText;
88 | }
89 | }
90 | catch (Exception ex)
91 | {
92 | r.ok = false;
93 | r.err = ex.Error();
94 | }
95 | return r;
96 | }
97 |
98 | }
99 |
100 |
101 | public class HclientProcessing : MessageProcessingHandler
102 | {
103 | public HclientProcessing()
104 | {
105 | InnerHandler = new HttpClientHandler();
106 | }
107 | protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
108 | {
109 | return request;
110 | }
111 | protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
112 | {
113 | return response;
114 | }
115 | }
116 |
117 | class HclientContent : HttpContent
118 | {
119 | private readonly Stream _innerContent;
120 | private readonly TaskInfo _task;
121 | private readonly long StreamLength;
122 | //private readonly ManualResetEvent _manualResetEvent;
123 | public HclientContent(Stream innerContent, TaskInfo task)
124 | {
125 | _innerContent = innerContent;
126 | if (task == null)
127 | {
128 | StreamLength = _innerContent.Length;
129 | }
130 | else
131 | {
132 | _task = task;
133 | StreamLength = task.Length > 0 ? task.Length : _innerContent.Length;
134 | if (task.Position > 0) _innerContent.Position = task.Position;
135 | }
136 | Headers.ContentLength = StreamLength;
137 | }
138 |
139 | public HclientContent(Stream innerContent) : this(innerContent, null)
140 | {
141 | }
142 | public HclientContent(byte[] bytes) : this(new MemoryStream(bytes))
143 | {
144 | }
145 | public HclientContent(byte[] bytes, TaskInfo task) : this(new MemoryStream(bytes), task)
146 | {
147 | }
148 | public HclientContent(string content) : this(content,null)
149 | {
150 | }
151 | public HclientContent(string content,TaskInfo task) : this(Encoding.UTF8.GetBytes(content),task)
152 | {
153 | Headers.ContentType = new MediaTypeHeaderValue("application/json");
154 | }
155 | protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
156 | {
157 | return Task.Run(() =>
158 | {
159 | bool taskrun = _task != null;
160 | long chunk = 0;
161 | long chunkLength = StreamLength;
162 | if (taskrun) chunkLength = StreamLength / 20;
163 | if (chunkLength == 0) chunkLength = StreamLength;
164 | long step = chunkLength;
165 | var buffer = new byte[chunkLength];
166 | using (_innerContent) while (true)
167 | {
168 | if (StreamLength - chunk < chunkLength) step = StreamLength - chunk;
169 | var length = _innerContent.Read(buffer, 0, (int)step);
170 | if (length <= 0) break;
171 | stream.Write(buffer, 0, length);
172 | if (taskrun)
173 | {
174 | chunk += length;
175 | _task.cancelToken.ThrowIfCancellationRequested();
176 | _task.Step = (int)(100 * chunk / StreamLength);
177 | }
178 | }
179 | });
180 | }
181 |
182 | protected override bool TryComputeLength(out long length)
183 | {
184 | length= _innerContent.Length;
185 | return true;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/aliyundrive/MsgBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace aliyundrive_Client_CSharp.aliyundrive
8 | {
9 | interface MagBase
10 | {
11 | string code { get; set; }
12 | string message { get; set; }
13 |
14 | }
15 | class HttpResult
16 | {
17 | public string code { get; set; }
18 | public string message { get; set; }
19 | public bool success { get; set; }
20 | public bool ok { get; set; }
21 | public string body { get; set; }
22 | public string err { get; set; }
23 | public T obj { get; set; }
24 | public string Error { get { return ok ? message : err; } }
25 | public bool Yes { get { return ok && success; } }
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/aliyundrive/Util.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Net.Http;
8 | using System.Reflection;
9 | using System.Security.Cryptography;
10 | using System.Text;
11 | using System.Text.RegularExpressions;
12 | using System.Threading.Tasks;
13 |
14 | namespace aliyundrive_Client_CSharp.aliyundrive
15 | {
16 | class DriveApi
17 | {
18 | public static string api = "https://api.aliyundrive.com/v2";
19 | public static string apiToken = "https://websv.aliyundrive.com";
20 |
21 | public static string token_refresh = apiToken + "/token/refresh";
22 | public static string file_list = api + "/file/list";
23 | public static string file_complete = api + "/file/complete";
24 | public static string file_search = api + "/file/search";
25 | public static string file_create = api + "/file/create";
26 | public static string file_update = api + "/file/update";
27 | public static string file_get = api + "/file/get";
28 | public static string file_share = api + "/share_link/create";
29 | public static string batch = api + "/batch";
30 | public static string databox_get_personal_info = api + "/databox/get_personal_info";
31 | }
32 | class Util
33 | {
34 | private static string _qrapi;
35 | public static string QRapi { get {
36 | if (_qrapi != null) return _qrapi;
37 | try
38 | {
39 | _qrapi = "https://gqrcode.alicdn.com/img?type=hv&text=";
40 | if (!new HttpClient().GetAsync(_qrapi + "qr").Result.IsSuccessStatusCode)
41 | throw new Exception();
42 | }
43 | catch (Exception)
44 | {
45 | _qrapi= "http://qr.js.cn/api/qr?qr=";
46 | }
47 | Console.WriteLine("_qrapi:{0}", _qrapi);
48 | return _qrapi;
49 | }
50 | }
51 | public static string GetQrUrl(string url)
52 | {
53 | return QRapi+ Encode(url);
54 | }
55 | public static string Encode(string str)
56 | {
57 | return Regex.Replace(str, "[^a-zA-Z0-9]", delegate (Match match) { return "%" + BitConverter.ToString(Encoding.UTF8.GetBytes(match.Value)).Replace("-", "%"); });
58 | }
59 | public string ConfigName = MethodBase.GetCurrentMethod().DeclaringType.FullName;
60 | public static string sha1(string data)
61 | {
62 | return BitConverter.ToString(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(data))).Replace("-", "");
63 | }
64 | public static string sha1(byte[] data)
65 | {
66 | return BitConverter.ToString(SHA1.Create().ComputeHash(data)).Replace("-", "");
67 | }
68 | public static string sha1(Stream stream)
69 | {
70 | return BitConverter.ToString(SHA1.Create().ComputeHash(stream)).Replace("-", "");
71 | }
72 | public static string sha1File(string FullName)
73 | {
74 | using (var stream = GetFileStream(FullName))
75 | return BitConverter.ToString(SHA1.Create().ComputeHash(stream)).Replace("-", "");
76 | }
77 |
78 | public static string Bin2Hex(byte[] bytes)
79 | {
80 | string returnStr = "";
81 | if (bytes != null)
82 | {
83 | for (int i = 0; i < bytes.Length; i++)
84 | {
85 | returnStr += bytes[i].ToString("X2");
86 | }
87 | }
88 | return returnStr;
89 | }
90 | public static byte[] Hex2Bin(string hexString)
91 | {
92 | if ((hexString.Length % 2) != 0)
93 | hexString += " ";
94 | byte[] returnBytes = new byte[hexString.Length / 2];
95 | for (int i = 0; i < returnBytes.Length; i++)
96 | returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
97 | return returnBytes;
98 | }
99 |
100 |
101 | public static Stream GetFileStream(string path)
102 | {
103 | return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
104 | }
105 | public static string GetShowSize(long size)
106 | {
107 | string[] units = new string[] { "B", "KB", "MB", "GB", "TB", "PB" };
108 | long mod = 1024;
109 | int i = 0;
110 | while (size >= mod)
111 | {
112 | size /= mod;
113 | i++;
114 | }
115 | return size + units[i];
116 | }
117 | }
118 |
119 | public static class MyExceptionEx
120 | {
121 | public static string Error(this Exception ex)
122 | {
123 | return GetFirstException(ex).Message;
124 | }
125 | public static Exception GetFirstException(Exception ex)
126 | {
127 | if (ex.InnerException == null) return ex;
128 | return GetFirstException(ex.InnerException);
129 | }
130 | }
131 | public class Config where T : new()
132 | {
133 | protected static object lock_obj { set; get; } = new object();
134 | protected static string AppDataPath { set; get; }
135 | static Config()
136 | {
137 | Assembly assembly = Assembly.GetExecutingAssembly();
138 | AssemblyProductAttribute product = assembly.GetCustomAttribute();
139 | AppDataPath = Path.Combine(
140 | Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
141 | "PlainWizard",
142 | product.Product
143 | );
144 | if (Directory.Exists(AppDataPath) == false)
145 | {
146 | Directory.CreateDirectory(AppDataPath);
147 | }
148 | }
149 | public Config()
150 | {
151 | }
152 | public void Config_Save()
153 | {
154 | try
155 | {
156 | lock (lock_obj) File.WriteAllText($"{AppDataPath}\\.Config.{typeof(T).FullName}.ini", JsonConvert.SerializeObject(this));
157 | }
158 | catch (Exception ex)
159 | {
160 | Console.Error.WriteLine(ex);
161 | }
162 | }
163 | public void Config_Save(string name)
164 | {
165 | lock (lock_obj) File.WriteAllText($"{AppDataPath}\\.Config.custom.{name}.ini", JsonConvert.SerializeObject(this));
166 | }
167 | public static T Config_Get(string name)
168 | {
169 | try
170 | {
171 | return JsonConvert.DeserializeObject(File.ReadAllText($"{AppDataPath}\\.Config.custom.{name}.ini"));
172 | }
173 | catch
174 | {
175 | return new T();
176 | }
177 | }
178 | protected static T _Instance;
179 | public static T Instance
180 | {
181 | get
182 | {
183 | if (_Instance == null)
184 | {
185 | lock (lock_obj)
186 | {
187 | if (_Instance == null)
188 | {
189 | try
190 | {
191 | _Instance = JsonConvert.DeserializeObject(File.ReadAllText($"{AppDataPath}\\.Config.{typeof(T).FullName}.ini"));
192 | }
193 | catch (Exception ex) { Console.WriteLine($"获取配置错误:{ex.Message}"); }
194 | }
195 | if (_Instance == null) _Instance = new T();
196 | }
197 | }
198 | return _Instance;
199 | }
200 | set
201 | {
202 | _Instance = value;
203 | }
204 | }
205 | }
206 | }
--------------------------------------------------------------------------------
/aliyundrive/databox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace aliyundrive_Client_CSharp.aliyundrive
8 | {
9 | class Databox_personal_rights_info
10 | {
11 | public string is_expires { get; set; }
12 | public string name { get; set; }
13 | public List privileges { get; set; }
14 | public string spu_id { get; set; }
15 |
16 | }
17 | class Databox_privileges
18 | {
19 | public string feature_id { get; set; }
20 | public string feature_attr_id { get; set; }
21 | public string speed_limit { get; set; }
22 | public long quota { get; set; }
23 | }
24 | class Databox_personal_space_info
25 | {
26 | public long used_size { get; set; }
27 | public long total_size { get; set; }
28 |
29 | }
30 | class databox : Config, MagBase
31 | {
32 | public string access_token { get; set; }
33 | public Databox_personal_rights_info personal_rights_info { get; set; }
34 | public Databox_personal_space_info personal_space_info { get; set; }
35 |
36 | public string code { get; set; }
37 | public string message { get; set; }
38 | public async Task> get_personal_info(bool redirect = false)
39 | {
40 | var client = new Hclient();
41 | var r = await client.PostAsJsonAsync(DriveApi.databox_get_personal_info, new { });
42 |
43 | if (r.success)
44 | {
45 | databox.Instance = r.obj;
46 | //databox.Instance.Config_Save();不保存这个了
47 | }
48 | else
49 | {
50 | Console.WriteLine($"获取个人信息失败:{r.Error}");
51 | }
52 | return r;
53 | }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/aliyundrive/file.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace aliyundrive_Client_CSharp.aliyundrive
11 | {
12 | class info_file : MagBase
13 | {
14 | public string code { get; set; }
15 | public string message { get; set; }
16 |
17 | public string drive_id { get; set; }
18 | public string domain_id { get; set; }
19 | public string file_id { get; set; }
20 | public string name { get; set; }
21 | public string type { get; set; }
22 | public DateTime created_at { get; set; }
23 | public DateTime updated_at { get; set; }
24 | public string file_extension { get; set; }
25 | public string status { get; set; }
26 | public long size { get; set; }
27 | public string upload_id { get; set; }
28 | public string parent_file_id { get; set; }
29 | public string crc64_hash { get; set; }
30 | public string content_hash { get; set; }
31 | public string content_hash_name { get; set; }
32 | public string download_url { get; set; }
33 | public string fields { get; set; }
34 |
35 | public async Task> get()
36 | {
37 | var client = new Hclient();
38 | var r = await client.PostAsJsonAsync(DriveApi.file_create, new
39 | {
40 | drive_id=token.Instance.default_drive_id,
41 | parent_file_id,
42 | file_id,
43 | fields,
44 | type,
45 | });
46 | return r;
47 | }
48 |
49 | }
50 | class file : MagBase
51 | {
52 | public string code { get; set; }
53 | public string message { get; set; }
54 | public int limit { get; set; } = 20;
55 | public string marker { get; set; }
56 | public string drive_id { get; set; }
57 | public string query { get; set; }
58 | public string parent_file_id { get; set; }
59 | public string image_thumbnail_process { get; set; }
60 | public string image_url_process { get; set; }
61 | public string video_thumbnail_process { get; set; }
62 | public string fields { get; set; }
63 | public string order_by { get; set; }
64 | public string order_direction { get; set; } = "DESC";
65 |
66 | public List items { get; set; }
67 | public string next_marker { get; set; }
68 | public async Task getfolderxxxxx(string parent_file_id, string name)
69 | {
70 | var f = new file();
71 | f.query = "parent_file_id=\"" + parent_file_id + "\" and type=\"folder\" and name=\"" + name + "\" ";
72 | var r = await f.search();
73 | if (!r.Yes) return "";
74 | if (r.obj.items.Count > 0) return r.obj.items[0].file_id;
75 | var r2 = await new upload().folder(parent_file_id, name, null);
76 | if (!r2.Yes) return "";
77 | return "";
78 | }
79 | /* 获取目录id原理:直接创建目录,已存在则忽略创建直接返回id
80 | {
81 | "parent_file_id": "root",
82 | "type": "folder",
83 | "file_id": "60476b1be0be64145d3a42ecb01a4c5a05dceea6",
84 | "domain_id": "bj29",
85 | "drive_id": "993274",
86 | "file_name": "sdddddd",
87 | "encrypt_mode": "none"
88 | }
89 | */
90 | public async Task> search()
91 | {
92 | //token.Instance.refresh_token = "xxxx";
93 | //var r = await token.Instance.refresh();
94 |
95 | var client = new Hclient();
96 | if (drive_id == null) drive_id = token.Instance.default_drive_id;
97 | if (parent_file_id == null) parent_file_id = "root";
98 | var r = await client.PostAsJsonAsync(DriveApi.file_search, new
99 | {
100 | limit,
101 | marker,
102 | drive_id,
103 | image_thumbnail_process,
104 | image_url_process,
105 | video_thumbnail_process,
106 | order_by,
107 | query
108 | });
109 | return r;
110 | }
111 | public async Task> search(string pfid, string name)
112 | {
113 | var client = new Hclient();
114 | if (drive_id == null) drive_id = token.Instance.default_drive_id;
115 | if (parent_file_id == null) parent_file_id = "root";
116 | query = $"parent_file_id='{pfid}' and name = '{name}'";
117 | var r = await client.PostAsJsonAsync(DriveApi.file_search, new
118 | {
119 | limit,
120 | marker,
121 | drive_id,
122 | query,
123 | order_direction
124 | });
125 | return r;
126 | }
127 |
128 | public async Task> list()
129 | {
130 | //token.Instance.refresh_token = "xxxx";
131 | //var r = await token.Instance.refresh();
132 | var client = new Hclient();
133 | if (drive_id == null) drive_id = token.Instance.default_drive_id;
134 | if (parent_file_id == null) parent_file_id = "root";
135 | var r = await client.PostAsJsonAsync(DriveApi.file_list, new
136 | {
137 | limit,
138 | marker,
139 | drive_id,
140 | parent_file_id,
141 | image_thumbnail_process,
142 | image_url_process,
143 | video_thumbnail_process,
144 | fields,
145 | order_by,
146 | order_direction
147 | });
148 | return r;
149 | }
150 | }
151 |
152 |
153 | class File_part_info_list: I_File_part_info_list
154 | {
155 | public int part_number { get; set; }
156 | public long part_size { get; set; }
157 | public string etag { get; set; }
158 | public string upload_url { get; set; }
159 | }
160 | interface I_File_part_info_list
161 | {
162 | int part_number { get; set; }
163 | string etag { get; set; }
164 | }
165 | class upload : MagBase
166 | {
167 | public string code { get; set; }
168 | public string message { get; set; }
169 | public string parent_file_id { get; set; }
170 | public List part_info_list { get; set; }
171 | public string upload_id { get; set; }
172 | public string upload_url { get; set; }
173 | public bool exist { get; set; }
174 | public bool rapid_upload { get; set; }
175 | public string status { get; set; }
176 | public string type { get; set; }
177 | public string file_id { get; set; }
178 | public string domain_id { get; set; }
179 | public string drive_id { get; set; }
180 | public string file_name { get; set; }
181 | public string encrypt_mode { get; set; }
182 | public string location { get; set; }
183 |
184 |
185 | public string name { get; set; }
186 | public string content_type { get; set; }
187 | public long size { get; set; }
188 | public string content_hash_name { get; set; }
189 | public string content_hash { get; set; }
190 | public bool ignoreError { get; set; }
191 | public string check_name_mode { get; set; }
192 | public string etag { get; set; }
193 | public async Task getfolder(string parent_file_id, string name)
194 | {
195 | var r = await new upload().folder(parent_file_id, name, null);
196 | if (r.Yes) return r.obj.file_id;
197 | throw new Exception($"创建目录[{name}]失败:{r.Error}");
198 | }
199 | public async Task> create()
200 | {
201 | var client = new Hclient();
202 | var r = await client.PostAsJsonAsync(DriveApi.file_create, new
203 | {
204 | name,
205 | type,
206 | content_type,
207 | size,
208 | drive_id,
209 | parent_file_id,
210 | part_info_list,
211 | content_hash_name,
212 | content_hash,
213 | ignoreError,
214 | check_name_mode
215 | });
216 | return r;
217 | }
218 | public async Task> put(string url, Stream stream, TaskInfo task)
219 | {
220 | var client = new Hclient();
221 | var r = await client.PutAsJsonAsync(url, stream,task);
222 | if (r.success) etag = r.body;
223 | return r;
224 | }
225 | public async Task> folder(string file_id, string name, TaskInfo task)
226 | {
227 | var f = this;
228 | f.check_name_mode = "refuse";// ignore, auto_rename, refuse.
229 | f.drive_id = token.Instance.default_drive_id;
230 | f.name = name;
231 | f.parent_file_id = file_id;
232 | f.type = "folder";
233 | var client = new Hclient();
234 | var r = await client.PostAsJsonAsync(DriveApi.file_create, new
235 | {
236 | name,
237 | type,
238 | drive_id,
239 | parent_file_id,
240 | check_name_mode
241 | });
242 | return r;
243 | }
244 | public async Task> rename(string file_id, string name, TaskInfo task)
245 | {
246 | var f = this;
247 | f.check_name_mode = "refuse";// ignore, auto_rename, refuse.
248 | f.drive_id = token.Instance.default_drive_id;
249 | f.file_id = file_id;
250 | f.name = name;
251 | var client = new Hclient();
252 | var r = await client.PostAsJsonAsync(DriveApi.file_update, new
253 | {
254 | name,
255 | drive_id,
256 | file_id,
257 | check_name_mode
258 | });
259 | return r;
260 | }
261 | public async Task> share(List file_id_list, string name, TaskInfo task)
262 | {
263 | var f = this;
264 | f.drive_id = token.Instance.default_drive_id;
265 | f.file_id = file_id;
266 | f.name = name;
267 | var client = new Hclient();
268 |
269 | var r = await client.PostAsJsonAsync(DriveApi.file_share, new
270 | {
271 | drive_id = drive_id,
272 | file_id_list = file_id_list,
273 | share_pwd = Guid.NewGuid().ToString().Substring(0, 4),
274 | expiration = "2030-04-15T17:13:58.720+08:00"
275 | });
276 | return r;
277 | }
278 | public async Task> Upload(string file_id, string name,TaskInfo task)
279 | {
280 | var f = this;
281 | f.check_name_mode = "ignore";// ignore, auto_rename, refuse.
282 | if (string.IsNullOrEmpty(task.sha1)|| task.size==0)
283 | {
284 | using (var stream = Util.GetFileStream(task.FullName))
285 | {
286 | f.content_hash = Util.sha1(stream);
287 | f.size = stream.Length;
288 | }
289 | }
290 | else
291 | {
292 | f.content_hash = task.sha1;
293 | f.size = task.size;
294 | }
295 | f.content_hash_name = "sha1";
296 | f.content_type = "application/octet-stream";
297 | f.drive_id = token.Instance.default_drive_id;
298 | f.ignoreError = false;
299 | f.name = name;
300 | f.parent_file_id = file_id;
301 | f.part_info_list = new List();
302 |
303 | int part_number = 0;
304 | long chunk = 0;
305 | long chunkSize = 5242880;//分包上传,每块大小,5M=5242880
306 | /*
307 | http://qr.js.cn/json.html
308 | {
309 | "code": "InvalidResource.PartList",
310 | "message": "The resource part_list is not valid. part entity too small"
311 | }
312 | */
313 | while (f.size > chunk)
314 | {
315 | chunk += chunkSize;
316 | if (chunk > f.size) chunkSize = f.size - chunk + chunkSize;
317 | part_number++;
318 | f.part_info_list.Add(new File_part_info_list { part_number= part_number, part_size = chunkSize });
319 | }
320 | f.type = "file";
321 | var r = await f.create();
322 | if (!r.success) return r;
323 | if (r.obj.rapid_upload|| r.obj.exist)
324 | {
325 | Console.WriteLine(r.body);
326 | //文件已存在,秒传思密达
327 | return r;
328 | }
329 | if (task.FullName == "") throw new Exception("无法秒传,需要文件上传");
330 | if (r.obj.part_info_list == null) throw new Exception("无需上传");
331 | List tasks = new List();
332 | long position = 0;
333 | task.Length = r.obj.part_info_list.Count;
334 | foreach (var item in r.obj.part_info_list)
335 | {
336 | TaskInfoUpload t = new TaskInfoUpload(task);
337 | t.Length = item.part_size;
338 | t.Position = position;
339 | position += t.Length;
340 | Console.WriteLine($"块上传,总:[{f.size}][{task.Length}],当前位置:[{t.Position}][{t.Length}],链接:{item.upload_url}");
341 |
342 | //tasks.Add(Task.Run(async() => {}));
343 | //不能用任务,会报错说上一部分还没传完,得按顺序传,估计服务器只能一个接一个组吧
344 | /*
345 | PartNotSequential
346 | For sequential multipart upload, you must upload or complete parts with sequential part number.
347 | */
348 |
349 | var r2 = await PutTask(item.upload_url, task.FullName, r, t);
350 | if (!r2.success)
351 | {
352 | if (r2.ok) return r2;
353 | //if (!r.success) break;
354 | //再给一次机会
355 | task.Step -= t.Step;
356 | r2 = await PutTask(item.upload_url, task.FullName, r, t);
357 | if (!r2.success)
358 | {
359 | return r2;
360 | //r = r2;
361 | //task.Cancel();
362 | //break;
363 | }
364 | }
365 | item.etag = r2.body;
366 | }
367 | //Task.WaitAll(tasks.ToArray());
368 | //if (!r.success) return r;
369 | var rc = await r.obj.complete();
370 | Console.WriteLine(rc.body);
371 | if (rc.success || rc.ok) return rc;
372 | //不成功再组一次
373 | return await r.obj.complete();
374 | }
375 | async Task> PutTask(string url,string FullName, HttpResult r, TaskInfo t)
376 | {
377 | using (var stream = Util.GetFileStream(FullName))
378 | {
379 | return await r.obj.put(url, stream, t);
380 | }
381 | }
382 | public async Task> complete()
383 | {
384 | var client = new Hclient();
385 | var r = await client.PostAsJsonAsync(DriveApi.file_complete, new
386 | {
387 | drive_id,
388 | file_id,
389 | ignoreError,
390 | part_info_list=part_info_list.Select(o=> new { o.part_number, o.etag }),
391 | upload_id,
392 | });
393 | return r;
394 | }
395 | public string fields { get; set; }
396 | }
397 | class TaskInfoUpload: TaskInfo
398 | {
399 | private readonly TaskInfo _task;
400 | private readonly int _stepNum;
401 | private int step;
402 | public TaskInfoUpload(TaskInfo task)
403 | {
404 | _task = task;
405 | _stepNum = (int)_task.Length;
406 | }
407 | public override int Step
408 | {
409 | set
410 | {
411 | var size = value / _stepNum;
412 | _task.Step += value - step;
413 | step = size;
414 | }
415 | get
416 | {
417 | return step;
418 | }
419 | }
420 | }
421 | class Type_batch_requests_body
422 | {
423 | public string drive_id { get; set; }
424 | public string file_id { get; set; }
425 | }
426 | class Type_batch_requests_headers
427 | {
428 | [JsonProperty("Content-Type")]
429 | public string ContentType { get; set; }
430 | }
431 | class Type_batch_requests
432 | {
433 | public Type_batch_requests_body body { get; set; }
434 | public Type_batch_requests_headers headers { get; set; }
435 | public string id { get; set; }
436 | public string method { get; set; }
437 | public string url { get; set; }
438 | }
439 | class batch : MagBase
440 | {
441 | public string code { get; set; }
442 | public string message { get; set; }
443 | public string resource { get; set; }
444 | public List requests { get; set; }
445 | public async Task> del(string id)
446 | {
447 | //token.Instance.refresh_token = "xxxx";
448 | //var r = await token.Instance.refresh();
449 | requests = new List();
450 | requests.Add(new Type_batch_requests
451 | {
452 | body = new Type_batch_requests_body
453 | {
454 | drive_id = token.Instance.default_drive_id,
455 | file_id = id,
456 | },
457 | headers = new Type_batch_requests_headers
458 | {
459 | ContentType = "application/json"
460 | },
461 | id = id,
462 | method = "DELETE",
463 | url = "/file/delete"
464 | });
465 | var client = new Hclient();
466 | var r = await client.PostAsJsonAsync(DriveApi.batch, new
467 | {
468 | requests,
469 | resource = "file"
470 | });
471 | return r;
472 | }
473 | }
474 | }
475 |
--------------------------------------------------------------------------------
/aliyundrive/token.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace aliyundrive_Client_CSharp.aliyundrive
10 | {
11 | class token : Config, MagBase
12 | {
13 | public string code { get; set; }
14 | public string message { get; set; }
15 | public string access_token { get; set; }
16 | public string refresh_token { get; set; }
17 | public long expires_in { get; set; }
18 | public string user_id { get; set; }
19 | public string user_name { get; set; }
20 | public string avatar { get; set; }
21 | public string nick_name { get; set; }
22 | public string default_drive_id { get; set; }
23 | public string default_sbox_drive_id { get; set; }
24 | public string state { get; set; }
25 | public string role { get; set; }
26 | public bool pin_setup { get; set; }
27 | public bool is_first_login { get; set; }
28 | public bool need_rp_verify { get; set; }
29 | public string device_id { get; set; }
30 | public static void SetToken(string str)
31 | {
32 | //"refresh_token":"72c8fa104efe4536816165dcebf1619c"
33 | var m=Regex.Match(str, "\"refresh_token\".+?\"(.+?)\"");
34 | if (!m.Success)
35 | {
36 | m = Regex.Match(str, "\\\\\"refresh_token\\\\\".+?\\\\\"(.+?)\\\\\"");
37 | }
38 | if (!m.Success)
39 | {
40 | m = Regex.Match(str, "([a-zA-Z0-9]{32})");
41 | }
42 | Instance.refresh_token = m.Success ? m.Groups[1].Value : "";
43 | }
44 | public async Task> Refresh()
45 | {
46 | Console.WriteLine($"正在刷新access_token");
47 | var client = new Hclient();
48 | var r = await client.PostAsJsonAsync(DriveApi.token_refresh, new
49 | {
50 | refresh_token
51 | },null,true);
52 | if (r.success)
53 | {
54 | Console.WriteLine($"获取access_token成功");
55 | Instance = r.obj;
56 | Instance.Config_Save();
57 | Console.WriteLine($"获取access_token成结束:{Instance.access_token},旧:{access_token}");
58 | }
59 | else
60 | {
61 | Console.WriteLine($"获取access_token失败:{r.body}");
62 | }
63 | return r;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlainWizard/aliyundrive-Client-CSharp/71dc852a2839aa08ea82d909ea2b77de962e4eef/favicon.ico
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlainWizard/aliyundrive-Client-CSharp/71dc852a2839aa08ea82d909ea2b77de962e4eef/windows.png
--------------------------------------------------------------------------------