chore: 同步修复华东节点上传文件的 bug
parent
ba39a81eb5
commit
991446940a
|
@ -23,7 +23,6 @@ namespace LeanCloud.Storage.Internal {
|
||||||
private AVFileController fileController;
|
private AVFileController fileController;
|
||||||
private AVObjectController objectController;
|
private AVObjectController objectController;
|
||||||
private AVQueryController queryController;
|
private AVQueryController queryController;
|
||||||
private AVSessionController sessionController;
|
|
||||||
private AVUserController userController;
|
private AVUserController userController;
|
||||||
private ObjectSubclassingController subclassingController;
|
private ObjectSubclassingController subclassingController;
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ namespace LeanCloud.Storage.Internal {
|
||||||
CloudCodeController = null;
|
CloudCodeController = null;
|
||||||
FileController = null;
|
FileController = null;
|
||||||
ObjectController = null;
|
ObjectController = null;
|
||||||
SessionController = null;
|
|
||||||
UserController = null;
|
UserController = null;
|
||||||
SubclassingController = null;
|
SubclassingController = null;
|
||||||
|
|
||||||
|
@ -98,21 +96,9 @@ namespace LeanCloud.Storage.Internal {
|
||||||
if (fileController != null) {
|
if (fileController != null) {
|
||||||
return fileController;
|
return fileController;
|
||||||
}
|
}
|
||||||
lock (mutex) {
|
fileController = new AVFileController();
|
||||||
switch (AVClient.CurrentConfiguration.RegionValue) {
|
|
||||||
case 0:
|
|
||||||
fileController = new QiniuFileController();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
fileController = new QCloudCosFileController();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
fileController = new AWSS3FileController();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return fileController;
|
return fileController;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
set {
|
set {
|
||||||
lock (mutex) {
|
lock (mutex) {
|
||||||
fileController = value;
|
fileController = value;
|
||||||
|
@ -150,20 +136,6 @@ namespace LeanCloud.Storage.Internal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AVSessionController SessionController {
|
|
||||||
get {
|
|
||||||
lock (mutex) {
|
|
||||||
sessionController = sessionController ?? new AVSessionController();
|
|
||||||
return sessionController;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
lock (mutex) {
|
|
||||||
sessionController = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AVUserController UserController {
|
public AVUserController UserController {
|
||||||
get {
|
get {
|
||||||
lock (mutex) {
|
lock (mutex) {
|
||||||
|
|
|
@ -8,12 +8,40 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
public abstract class AVFileController {
|
public interface IFileUploader {
|
||||||
public abstract Task<FileState> SaveAsync(FileState state,
|
Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AVFileController {
|
||||||
|
const string QCloud = "qcloud";
|
||||||
|
const string AWS = "s3";
|
||||||
|
|
||||||
|
public Task<FileState> SaveAsync(FileState state,
|
||||||
Stream dataStream,
|
Stream dataStream,
|
||||||
String sessionToken,
|
String sessionToken,
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
IProgress<AVUploadProgressEventArgs> progress,
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
|
if (state.Url != null) {
|
||||||
|
// !isDirty
|
||||||
|
return Task<FileState>.FromResult(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetFileToken(state, cancellationToken).OnSuccess(t => {
|
||||||
|
// 根据 provider 区分 cdn
|
||||||
|
var ret = t.Result;
|
||||||
|
var fileToken = ret.Item2;
|
||||||
|
var provider = fileToken["provider"] as string;
|
||||||
|
switch (provider) {
|
||||||
|
case QCloud:
|
||||||
|
return new QCloudUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||||
|
case AWS:
|
||||||
|
return new AWSUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||||
|
default:
|
||||||
|
return new QiniuUploader().Upload(state, dataStream, fileToken, progress, cancellationToken);
|
||||||
|
}
|
||||||
|
}).Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) {
|
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) {
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
|
@ -24,7 +52,6 @@ namespace LeanCloud.Storage.Internal {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
|
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
|
||||||
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
|
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
string currentSessionToken = AVUser.CurrentSessionToken;
|
||||||
string str = fileState.Name;
|
string str = fileState.Name;
|
||||||
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
IDictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
|
|
|
@ -6,21 +6,14 @@ using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
internal class AWSS3FileController : AVFileController {
|
internal class AWSUploader : IFileUploader {
|
||||||
public override Task<FileState> SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) {
|
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress,
|
||||||
if (state.Url != null) {
|
CancellationToken cancellationToken) {
|
||||||
return Task.FromResult(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetFileToken(state, cancellationToken).OnSuccess(t => {
|
|
||||||
var fileToken = t.Result.Item2;
|
|
||||||
var uploadUrl = fileToken["upload_url"].ToString();
|
var uploadUrl = fileToken["upload_url"].ToString();
|
||||||
state.ObjectId = fileToken["objectId"].ToString();
|
state.ObjectId = fileToken["objectId"].ToString();
|
||||||
string url = fileToken["url"] as string;
|
string url = fileToken["url"] as string;
|
||||||
state.Url = new Uri(url, UriKind.Absolute);
|
state.Url = new Uri(url, UriKind.Absolute);
|
||||||
return PutFile(state, uploadUrl, dataStream);
|
return PutFile(state, uploadUrl, dataStream).OnSuccess(s => {
|
||||||
|
|
||||||
}).Unwrap().OnSuccess(s => {
|
|
||||||
return s.Result;
|
return s.Result;
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -6,10 +6,9 @@ using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
internal class QCloudCosFileController : AVFileController {
|
internal class QCloudUploader : IFileUploader {
|
||||||
private object mutex = new object();
|
private object mutex = new object();
|
||||||
|
|
||||||
FileState fileState;
|
FileState fileState;
|
||||||
|
@ -20,18 +19,9 @@ namespace LeanCloud.Storage.Internal {
|
||||||
bool done;
|
bool done;
|
||||||
private long sliceSize = (long)CommonSize.KB512;
|
private long sliceSize = (long)CommonSize.KB512;
|
||||||
|
|
||||||
public override Task<FileState> SaveAsync(FileState state,
|
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken) {
|
||||||
Stream dataStream,
|
|
||||||
string sessionToken,
|
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
|
||||||
CancellationToken cancellationToken) {
|
|
||||||
if (state.Url != null) {
|
|
||||||
return Task<FileState>.FromResult(state);
|
|
||||||
}
|
|
||||||
fileState = state;
|
fileState = state;
|
||||||
data = dataStream;
|
data = dataStream;
|
||||||
return GetFileToken(fileState, cancellationToken).OnSuccess(_ => {
|
|
||||||
var fileToken = _.Result.Item2;
|
|
||||||
uploadUrl = fileToken["upload_url"].ToString();
|
uploadUrl = fileToken["upload_url"].ToString();
|
||||||
token = fileToken["token"].ToString();
|
token = fileToken["token"].ToString();
|
||||||
fileState.ObjectId = fileToken["objectId"].ToString();
|
fileState.ObjectId = fileToken["objectId"].ToString();
|
||||||
|
@ -46,8 +36,6 @@ namespace LeanCloud.Storage.Internal {
|
||||||
var sliceOffset = long.Parse(resumeData["offset"].ToString());
|
var sliceOffset = long.Parse(resumeData["offset"].ToString());
|
||||||
return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken);
|
return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
|
|
||||||
}).Unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<FileState> UploadSlice(
|
Task<FileState> UploadSlice(
|
|
@ -9,104 +9,75 @@ using System.Threading.Tasks;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
internal enum CommonSize : long {
|
||||||
internal enum CommonSize : long
|
|
||||||
{
|
|
||||||
MB4 = 1024 * 1024 * 4,
|
MB4 = 1024 * 1024 * 4,
|
||||||
MB1 = 1024 * 1024,
|
MB1 = 1024 * 1024,
|
||||||
KB512 = 1024 * 1024 / 2,
|
KB512 = 1024 * 1024 / 2,
|
||||||
KB256 = 1024 * 1024 / 4
|
KB256 = 1024 * 1024 / 4
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class QiniuFileController : AVFileController
|
internal class QiniuUploader : IFileUploader {
|
||||||
{
|
|
||||||
private static int BLOCKSIZE = 1024 * 1024 * 4;
|
private static int BLOCKSIZE = 1024 * 1024 * 4;
|
||||||
private const int blockMashk = (1 << blockBits) - 1;
|
private const int blockMashk = (1 << blockBits) - 1;
|
||||||
private const int blockBits = 22;
|
private const int blockBits = 22;
|
||||||
private int CalcBlockCount(long fsize)
|
private int CalcBlockCount(long fsize) {
|
||||||
{
|
|
||||||
return (int)((fsize + blockMashk) >> blockBits);
|
return (int)((fsize + blockMashk) >> blockBits);
|
||||||
}
|
}
|
||||||
internal static string UP_HOST = "https://up.qbox.me";
|
internal static string UP_HOST = "https://up.qbox.me";
|
||||||
private object mutex = new object();
|
private object mutex = new object();
|
||||||
|
|
||||||
public override Task<FileState> SaveAsync(FileState state,
|
public Task<FileState> Upload(FileState state, Stream dataStream, IDictionary<string, object> fileToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken) {
|
||||||
Stream dataStream,
|
|
||||||
String sessionToken,
|
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (state.Url != null)
|
|
||||||
{
|
|
||||||
return Task<FileState>.FromResult(state);
|
|
||||||
}
|
|
||||||
state.frozenData = dataStream;
|
state.frozenData = dataStream;
|
||||||
state.CloudName = GetUniqueName(state);
|
state.CloudName = GetUniqueName(state);
|
||||||
return GetQiniuToken(state, CancellationToken.None).ContinueWith(t =>
|
MergeFromJSON(state, fileToken);
|
||||||
{
|
return UploadNextChunk(state, dataStream, string.Empty, 0, progress).OnSuccess(_ => {
|
||||||
MergeFromJSON(state, t.Result.Item2);
|
|
||||||
return UploadNextChunk(state, dataStream, string.Empty, 0, progress);
|
|
||||||
}).Unwrap().OnSuccess<FileState>(s =>
|
|
||||||
{
|
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress<AVUploadProgressEventArgs> progress)
|
|
||||||
{
|
Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress<AVUploadProgressEventArgs> progress) {
|
||||||
var totalSize = dataStream.Length;
|
var totalSize = dataStream.Length;
|
||||||
var remainingSize = totalSize - state.completed;
|
var remainingSize = totalSize - state.completed;
|
||||||
|
|
||||||
if (progress != null)
|
if (progress != null) {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
progress.Report(new AVUploadProgressEventArgs() {
|
||||||
{
|
|
||||||
progress.Report(new AVUploadProgressEventArgs()
|
|
||||||
{
|
|
||||||
Progress = AVFileController.CalcProgress(state.completed, totalSize)
|
Progress = AVFileController.CalcProgress(state.completed, totalSize)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.completed == totalSize)
|
if (state.completed == totalSize) {
|
||||||
{
|
|
||||||
return QiniuMakeFile(state, state.frozenData, state.token, state.CloudName, totalSize, state.block_ctxes.ToArray(), CancellationToken.None);
|
return QiniuMakeFile(state, state.frozenData, state.token, state.CloudName, totalSize, state.block_ctxes.ToArray(), CancellationToken.None);
|
||||||
|
|
||||||
}
|
} else if (state.completed % BLOCKSIZE == 0) {
|
||||||
else if (state.completed % BLOCKSIZE == 0)
|
|
||||||
{
|
|
||||||
var firstChunkBinary = GetChunkBinary(state.completed, dataStream);
|
var firstChunkBinary = GetChunkBinary(state.completed, dataStream);
|
||||||
|
|
||||||
var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize;
|
var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize;
|
||||||
return MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t =>
|
return MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t => {
|
||||||
{
|
|
||||||
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
|
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
|
||||||
var ctx = dict["ctx"].ToString();
|
var ctx = dict["ctx"].ToString();
|
||||||
offset = long.Parse(dict["offset"].ToString());
|
offset = long.Parse(dict["offset"].ToString());
|
||||||
var host = dict["host"].ToString();
|
var host = dict["host"].ToString();
|
||||||
|
|
||||||
state.completed += firstChunkBinary.Length;
|
state.completed += firstChunkBinary.Length;
|
||||||
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
|
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) {
|
||||||
{
|
|
||||||
state.block_ctxes.Add(ctx);
|
state.block_ctxes.Add(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UploadNextChunk(state, dataStream, ctx, offset, progress);
|
return UploadNextChunk(state, dataStream, ctx, offset, progress);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
|
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
var chunkBinary = GetChunkBinary(state.completed, dataStream);
|
var chunkBinary = GetChunkBinary(state.completed, dataStream);
|
||||||
return PutChunk(state, chunkBinary, context, offset).ContinueWith(t =>
|
return PutChunk(state, chunkBinary, context, offset).ContinueWith(t => {
|
||||||
{
|
|
||||||
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
|
var dict = JsonConvert.DeserializeObject<IDictionary<string, object>>(t.Result.Item2, new LeanCloudJsonConverter());
|
||||||
var ctx = dict["ctx"].ToString();
|
var ctx = dict["ctx"].ToString();
|
||||||
|
|
||||||
offset = long.Parse(dict["offset"].ToString());
|
offset = long.Parse(dict["offset"].ToString());
|
||||||
var host = dict["host"].ToString();
|
var host = dict["host"].ToString();
|
||||||
state.completed += chunkBinary.Length;
|
state.completed += chunkBinary.Length;
|
||||||
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
|
if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) {
|
||||||
{
|
|
||||||
state.block_ctxes.Add(ctx);
|
state.block_ctxes.Add(ctx);
|
||||||
}
|
}
|
||||||
//if (AVClient.fileUploaderDebugLog)
|
//if (AVClient.fileUploaderDebugLog)
|
||||||
|
@ -119,11 +90,9 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] GetChunkBinary(long completed, Stream dataStream)
|
byte[] GetChunkBinary(long completed, Stream dataStream) {
|
||||||
{
|
|
||||||
long chunkSize = (long)CommonSize.MB1;
|
long chunkSize = (long)CommonSize.MB1;
|
||||||
if (completed + chunkSize > dataStream.Length)
|
if (completed + chunkSize > dataStream.Length) {
|
||||||
{
|
|
||||||
chunkSize = dataStream.Length - completed;
|
chunkSize = dataStream.Length - completed;
|
||||||
}
|
}
|
||||||
byte[] chunkBinary = new byte[chunkSize];
|
byte[] chunkBinary = new byte[chunkSize];
|
||||||
|
@ -132,16 +101,13 @@ namespace LeanCloud.Storage.Internal
|
||||||
return chunkBinary;
|
return chunkBinary;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string GetUniqueName(FileState state)
|
internal string GetUniqueName(FileState state) {
|
||||||
{
|
|
||||||
string key = Guid.NewGuid().ToString();//file Key in Qiniu.
|
string key = Guid.NewGuid().ToString();//file Key in Qiniu.
|
||||||
string extension = Path.GetExtension(state.Name);
|
string extension = Path.GetExtension(state.Name);
|
||||||
key += extension;
|
key += extension;
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetQiniuToken(FileState state, CancellationToken cancellationToken)
|
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetQiniuToken(FileState state, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
|
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
string currentSessionToken = AVUser.CurrentSessionToken;
|
||||||
string str = state.Name;
|
string str = state.Name;
|
||||||
|
|
||||||
|
@ -162,8 +128,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||||
}
|
}
|
||||||
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state)
|
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state) {
|
||||||
{
|
|
||||||
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
|
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
|
||||||
|
|
||||||
string authHead = "UpToken " + state.token;
|
string authHead = "UpToken " + state.token;
|
||||||
|
@ -212,19 +177,16 @@ namespace LeanCloud.Storage.Internal
|
||||||
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
|
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Tuple<HttpStatusCode, string>> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken)
|
internal async Task<Tuple<HttpStatusCode, string>> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
StringBuilder urlBuilder = new StringBuilder();
|
StringBuilder urlBuilder = new StringBuilder();
|
||||||
urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize);
|
urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize);
|
||||||
if (key != null)
|
if (key != null) {
|
||||||
{
|
|
||||||
urlBuilder.AppendFormat("/key/{0}", ToBase64URLSafe(key));
|
urlBuilder.AppendFormat("/key/{0}", ToBase64URLSafe(key));
|
||||||
}
|
}
|
||||||
var metaData = GetMetaData(state, dataStream);
|
var metaData = GetMetaData(state, dataStream);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
foreach (string _key in metaData.Keys)
|
foreach (string _key in metaData.Keys) {
|
||||||
{
|
|
||||||
sb.AppendFormat("/{0}/{1}", _key, ToBase64URLSafe(metaData[_key].ToString()));
|
sb.AppendFormat("/{0}/{1}", _key, ToBase64URLSafe(metaData[_key].ToString()));
|
||||||
}
|
}
|
||||||
urlBuilder.Append(sb.ToString());
|
urlBuilder.Append(sb.ToString());
|
||||||
|
@ -238,12 +200,10 @@ namespace LeanCloud.Storage.Internal
|
||||||
int proCount = ctxes.Length;
|
int proCount = ctxes.Length;
|
||||||
Stream body = new MemoryStream();
|
Stream body = new MemoryStream();
|
||||||
|
|
||||||
for (int i = 0; i < proCount; i++)
|
for (int i = 0; i < proCount; i++) {
|
||||||
{
|
|
||||||
byte[] bctx = StringToAscii(ctxes[i]);
|
byte[] bctx = StringToAscii(ctxes[i]);
|
||||||
body.Write(bctx, 0, bctx.Length);
|
body.Write(bctx, 0, bctx.Length);
|
||||||
if (i != proCount - 1)
|
if (i != proCount - 1) {
|
||||||
{
|
|
||||||
body.WriteByte((byte)',');
|
body.WriteByte((byte)',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,10 +223,8 @@ namespace LeanCloud.Storage.Internal
|
||||||
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
|
return await JsonUtils.DeserializeObjectAsync<Tuple<HttpStatusCode, string>>(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData)
|
internal void MergeFromJSON(FileState state, IDictionary<string, object> jsonData) {
|
||||||
{
|
lock (this.mutex) {
|
||||||
lock (this.mutex)
|
|
||||||
{
|
|
||||||
string url = jsonData["url"] as string;
|
string url = jsonData["url"] as string;
|
||||||
state.Url = new Uri(url, UriKind.Absolute);
|
state.Url = new Uri(url, UriKind.Absolute);
|
||||||
state.bucketId = FetchBucketId(url);
|
state.bucketId = FetchBucketId(url);
|
||||||
|
@ -276,17 +234,14 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string FetchBucketId(string url)
|
string FetchBucketId(string url) {
|
||||||
{
|
|
||||||
var elements = url.Split('/');
|
var elements = url.Split('/');
|
||||||
|
|
||||||
return elements[elements.Length - 1];
|
return elements[elements.Length - 1];
|
||||||
}
|
}
|
||||||
public static byte[] StringToAscii(string s)
|
public static byte[] StringToAscii(string s) {
|
||||||
{
|
|
||||||
byte[] retval = new byte[s.Length];
|
byte[] retval = new byte[s.Length];
|
||||||
for (int ix = 0; ix < s.Length; ++ix)
|
for (int ix = 0; ix < s.Length; ++ix) {
|
||||||
{
|
|
||||||
char ch = s[ix];
|
char ch = s[ix];
|
||||||
if (ch <= 0x7f)
|
if (ch <= 0x7f)
|
||||||
retval[ix] = (byte)ch;
|
retval[ix] = (byte)ch;
|
||||||
|
@ -295,20 +250,17 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
public static string ToBase64URLSafe(string str)
|
public static string ToBase64URLSafe(string str) {
|
||||||
{
|
|
||||||
return Encode(str);
|
return Encode(str);
|
||||||
}
|
}
|
||||||
public static string Encode(byte[] bs)
|
public static string Encode(byte[] bs) {
|
||||||
{
|
|
||||||
if (bs == null || bs.Length == 0)
|
if (bs == null || bs.Length == 0)
|
||||||
return "";
|
return "";
|
||||||
string encodedStr = Convert.ToBase64String(bs);
|
string encodedStr = Convert.ToBase64String(bs);
|
||||||
encodedStr = encodedStr.Replace('+', '-').Replace('/', '_');
|
encodedStr = encodedStr.Replace('+', '-').Replace('/', '_');
|
||||||
return encodedStr;
|
return encodedStr;
|
||||||
}
|
}
|
||||||
public static string Encode(string text)
|
public static string Encode(string text) {
|
||||||
{
|
|
||||||
if (String.IsNullOrEmpty(text))
|
if (String.IsNullOrEmpty(text))
|
||||||
return "";
|
return "";
|
||||||
byte[] bs = Encoding.UTF8.GetBytes(text);
|
byte[] bs = Encoding.UTF8.GetBytes(text);
|
||||||
|
@ -317,28 +269,23 @@ namespace LeanCloud.Storage.Internal
|
||||||
return encodedStr;
|
return encodedStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string GetMD5Code(Stream data)
|
internal static string GetMD5Code(Stream data) {
|
||||||
{
|
|
||||||
MD5 md5 = new MD5CryptoServiceProvider();
|
MD5 md5 = new MD5CryptoServiceProvider();
|
||||||
byte[] retVal = md5.ComputeHash(data);
|
byte[] retVal = md5.ComputeHash(data);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int i = 0; i < retVal.Length; i++)
|
for (int i = 0; i < retVal.Length; i++) {
|
||||||
{
|
|
||||||
sb.Append(retVal[i].ToString("x2"));
|
sb.Append(retVal[i].ToString("x2"));
|
||||||
}
|
}
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDictionary<string, object> GetMetaData(FileState state, Stream data)
|
internal IDictionary<string, object> GetMetaData(FileState state, Stream data) {
|
||||||
{
|
|
||||||
IDictionary<string, object> rtn = new Dictionary<string, object>();
|
IDictionary<string, object> rtn = new Dictionary<string, object>();
|
||||||
|
|
||||||
if (state.MetaData != null)
|
if (state.MetaData != null) {
|
||||||
{
|
foreach (var meta in state.MetaData) {
|
||||||
foreach (var meta in state.MetaData)
|
|
||||||
{
|
|
||||||
rtn.Add(meta.Key, meta.Value);
|
rtn.Add(meta.Key, meta.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,14 +298,10 @@ namespace LeanCloud.Storage.Internal
|
||||||
|
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
internal void MergeDic(IDictionary<string, object> dic, string key, object value)
|
internal void MergeDic(IDictionary<string, object> dic, string key, object value) {
|
||||||
{
|
if (dic.ContainsKey(key)) {
|
||||||
if (dic.ContainsKey(key))
|
|
||||||
{
|
|
||||||
dic[key] = value;
|
dic[key] = value;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
dic.Add(key, value);
|
dic.Add(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -97,9 +97,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
registerActions.TryGetValue(className, out toPerform);
|
registerActions.TryGetValue(className, out toPerform);
|
||||||
mutex.ExitReadLock();
|
mutex.ExitReadLock();
|
||||||
|
|
||||||
if (toPerform != null) {
|
toPerform?.Invoke();
|
||||||
toPerform();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnregisterSubclass(Type type) {
|
public void UnregisterSubclass(Type type) {
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public class AVSessionController {
|
|
||||||
public Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
|
||||||
var command = new AVCommand {
|
|
||||||
Path = "sessions/me",
|
|
||||||
Method = HttpMethod.Get
|
|
||||||
};
|
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) {
|
|
||||||
var command = new AVCommand {
|
|
||||||
Path = "logout",
|
|
||||||
Method = HttpMethod.Post
|
|
||||||
};
|
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
|
||||||
var command = new AVCommand {
|
|
||||||
Path = "upgradeToRevocableSession",
|
|
||||||
Method = HttpMethod.Post,
|
|
||||||
};
|
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRevocableSessionToken(string sessionToken) {
|
|
||||||
return sessionToken.Contains("r:");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc.
|
|
||||||
///
|
|
||||||
/// These cannot be 'internal' anymore if we are fully modularizing things out, because
|
|
||||||
/// they are no longer a part of the same library, especially as we create things like
|
|
||||||
/// Installation inside push library.
|
|
||||||
///
|
|
||||||
/// So this class contains a bunch of extension methods that can live inside another
|
|
||||||
/// namespace, which 'wrap' the intenral APIs that already exist.
|
|
||||||
/// </summary>
|
|
||||||
public static class AVSessionExtensions {
|
|
||||||
public static Task<string> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
|
||||||
return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) {
|
|
||||||
return AVSession.RevokeAsync(sessionToken, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -251,7 +251,6 @@ namespace LeanCloud {
|
||||||
|
|
||||||
AVObject.RegisterSubclass<AVUser>();
|
AVObject.RegisterSubclass<AVUser>();
|
||||||
AVObject.RegisterSubclass<AVRole>();
|
AVObject.RegisterSubclass<AVRole>();
|
||||||
AVObject.RegisterSubclass<AVSession>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Config(Configuration configuration) {
|
internal static void Config(Configuration configuration) {
|
||||||
|
|
|
@ -350,129 +350,4 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AVRPCC loud function base.
|
|
||||||
/// </summary>
|
|
||||||
public class AVRPCCloudFunctionBase<P, R> {
|
|
||||||
/// <summary>
|
|
||||||
/// AVRPCD eserialize.
|
|
||||||
/// </summary>
|
|
||||||
public delegate R AVRPCDeserialize<R>(IDictionary<string, object> result);
|
|
||||||
/// <summary>
|
|
||||||
/// AVRPCS erialize.
|
|
||||||
/// </summary>
|
|
||||||
public delegate IDictionary<string, object> AVRPCSerialize<P>(P parameters);
|
|
||||||
|
|
||||||
public AVRPCCloudFunctionBase()
|
|
||||||
: this(true) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public AVRPCCloudFunctionBase(bool noneParameters) {
|
|
||||||
if (noneParameters) {
|
|
||||||
this.Encode = n => {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private AVRPCDeserialize<R> _decode;
|
|
||||||
public AVRPCDeserialize<R> Decode {
|
|
||||||
get {
|
|
||||||
return _decode;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_decode = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private AVRPCSerialize<P> _encode;
|
|
||||||
public AVRPCSerialize<P> Encode {
|
|
||||||
get {
|
|
||||||
if (_encode == null) {
|
|
||||||
_encode = n => {
|
|
||||||
if (n != null) {
|
|
||||||
return JsonConvert.DeserializeObject<Dictionary<string, object>>(n.ToString(), new LeanCloudJsonConverter());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return _encode;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
_encode = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
public string FunctionName { get; set; }
|
|
||||||
|
|
||||||
public Task<R> ExecuteAsync(P parameters) {
|
|
||||||
var encodedParameters = Encode(parameters);
|
|
||||||
var command = new EngineCommand {
|
|
||||||
Path = $"call/{Uri.EscapeUriString(FunctionName)}",
|
|
||||||
Method = HttpMethod.Post,
|
|
||||||
Content = encodedParameters
|
|
||||||
};
|
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command).OnSuccess(s => {
|
|
||||||
var responseBody = s.Result.Item2;
|
|
||||||
if (!responseBody.ContainsKey("result")) {
|
|
||||||
return default(R);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Decode(responseBody);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class AVObjectRPCCloudFunction : AVObjectRPCCloudFunction<AVObject> {
|
|
||||||
|
|
||||||
}
|
|
||||||
public class AVObjectListRPCCloudFunction : AVObjectListRPCCloudFunction<AVObject> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AVObjectListRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, IList<R>> where R : AVObject {
|
|
||||||
public AVObjectListRPCCloudFunction()
|
|
||||||
: base(true) {
|
|
||||||
this.Decode = this.AVObjectListDeserializer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AVRPCDeserialize<IList<R>> AVObjectListDeserializer() {
|
|
||||||
AVRPCDeserialize<IList<R>> del = data => {
|
|
||||||
var items = data["result"] as IList<object>;
|
|
||||||
|
|
||||||
return items.Select(item => {
|
|
||||||
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
|
||||||
return AVObject.FromState<AVObject>(state, state.ClassName);
|
|
||||||
}).ToList() as IList<R>;
|
|
||||||
|
|
||||||
};
|
|
||||||
return del;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AVObjectRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, R> where R : AVObject {
|
|
||||||
public AVObjectRPCCloudFunction()
|
|
||||||
: base(true) {
|
|
||||||
this.Decode = this.AVObjectDeserializer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public AVRPCDeserialize<R> AVObjectDeserializer() {
|
|
||||||
AVRPCDeserialize<R> del = data => {
|
|
||||||
var item = data["result"] as object;
|
|
||||||
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
|
||||||
|
|
||||||
return AVObject.FromState<R>(state, state.ClassName);
|
|
||||||
};
|
|
||||||
return del;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a session of a user for a LeanCloud application.
|
|
||||||
/// </summary>
|
|
||||||
[AVClassName("_Session")]
|
|
||||||
public class AVSession : AVObject
|
|
||||||
{
|
|
||||||
private static readonly HashSet<string> readOnlyKeys = new HashSet<string> {
|
|
||||||
"sessionToken", "createdWith", "restricted", "user", "expiresAt", "installationId"
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override bool IsKeyMutable(string key)
|
|
||||||
{
|
|
||||||
return !readOnlyKeys.Contains(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the session token for a user, if they are logged in.
|
|
||||||
/// </summary>
|
|
||||||
[AVFieldName("sessionToken")]
|
|
||||||
public string SessionToken
|
|
||||||
{
|
|
||||||
get { return GetProperty<string>(null, "SessionToken"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a <see cref="AVQuery{AVSession}"/> for AVSession.
|
|
||||||
/// </summary>
|
|
||||||
public static AVQuery<AVSession> Query
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new AVQuery<AVSession>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AVSessionController SessionController
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return AVPlugins.Instance.SessionController;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current <see cref="AVSession"/> object related to the current user.
|
|
||||||
/// </summary>
|
|
||||||
public static Task<AVSession> GetCurrentSessionAsync()
|
|
||||||
{
|
|
||||||
return GetCurrentSessionAsync(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current <see cref="AVSession"/> object related to the current user.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token</param>
|
|
||||||
public static Task<AVSession> GetCurrentSessionAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
AVUser user = AVUser.CurrentUser;
|
|
||||||
if (user == null) {
|
|
||||||
return Task<AVSession>.FromResult((AVSession)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
string sessionToken = user.SessionToken;
|
|
||||||
if (sessionToken == null) {
|
|
||||||
return Task<AVSession>.FromResult((AVSession)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SessionController.GetSessionAsync(sessionToken, cancellationToken).OnSuccess(t =>
|
|
||||||
{
|
|
||||||
AVSession session = AVObject.FromState<AVSession>(t.Result, "_Session");
|
|
||||||
return session;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (sessionToken == null || !SessionController.IsRevocableSessionToken(sessionToken))
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
return SessionController.RevokeAsync(sessionToken, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Task<string> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (sessionToken == null || SessionController.IsRevocableSessionToken(sessionToken))
|
|
||||||
{
|
|
||||||
return Task<string>.FromResult(sessionToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SessionController.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken).OnSuccess(t =>
|
|
||||||
{
|
|
||||||
AVSession session = AVObject.FromState<AVSession>(t.Result, "_Session");
|
|
||||||
return session.SessionToken;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -343,47 +343,12 @@ namespace LeanCloud {
|
||||||
/// Typically, you should use <see cref="LogOutAsync()"/>, unless you are managing your own threading.
|
/// Typically, you should use <see cref="LogOutAsync()"/>, unless you are managing your own threading.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static void LogOut() {
|
public static void LogOut() {
|
||||||
|
CurrentUser = null;
|
||||||
|
|
||||||
// TODO (hallucinogen): this will without a doubt fail in Unity. But what else can we do?
|
// TODO (hallucinogen): this will without a doubt fail in Unity. But what else can we do?
|
||||||
//LogOutAsync().Wait();
|
//LogOutAsync().Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
|
|
||||||
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
|
|
||||||
/// background thread.
|
|
||||||
/// </remarks>
|
|
||||||
public static void LogOutAsync() {
|
|
||||||
//return LogOutAsync(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Logs out the currently logged in user session. This will remove the session from disk, log out of
|
|
||||||
/// linked services, and future calls to <see cref="CurrentUser"/> will return <c>null</c>.
|
|
||||||
///
|
|
||||||
/// This is preferable to using <see cref="LogOut()"/>, unless your code is already running from a
|
|
||||||
/// background thread.
|
|
||||||
/// </summary>
|
|
||||||
public static void LogOutAsync(CancellationToken cancellationToken) {
|
|
||||||
CurrentUser = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) {
|
|
||||||
// string oldSessionToken = SessionToken;
|
|
||||||
// if (oldSessionToken == null) {
|
|
||||||
// return Task.FromResult(0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Cleanup in-memory session.
|
|
||||||
// MutateState(mutableClone => {
|
|
||||||
// mutableClone.ServerData.Remove("sessionToken");
|
|
||||||
// });
|
|
||||||
// var revokeSessionTask = AVSession.RevokeAsync(oldSessionToken, cancellationToken);
|
|
||||||
// return Task.WhenAll(revokeSessionTask, CurrentUserController.LogOutAsync(cancellationToken));
|
|
||||||
//}
|
|
||||||
|
|
||||||
private static void LogOutWithProviders() {
|
private static void LogOutWithProviders() {
|
||||||
foreach (var provider in authProviders.Values) {
|
foreach (var provider in authProviders.Values) {
|
||||||
provider.Deauthenticate();
|
provider.Deauthenticate();
|
||||||
|
@ -391,8 +356,8 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AVUser CurrentUser {
|
public static AVUser CurrentUser {
|
||||||
// TODO 线程问题
|
get;
|
||||||
get; internal set;
|
internal set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -404,27 +369,6 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Legacy / Revocable Session Tokens
|
|
||||||
|
|
||||||
private static readonly object isRevocableSessionEnabledMutex = new object();
|
|
||||||
private static bool isRevocableSessionEnabled;
|
|
||||||
|
|
||||||
internal static void DisableRevocableSession() {
|
|
||||||
lock (isRevocableSessionEnabledMutex) {
|
|
||||||
isRevocableSessionEnabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsRevocableSessionEnabled {
|
|
||||||
get {
|
|
||||||
lock (isRevocableSessionEnabledMutex) {
|
|
||||||
return isRevocableSessionEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Requests a password reset email to be sent to the specified email address associated with the
|
/// Requests a password reset email to be sent to the specified email address associated with the
|
||||||
/// user account. This email allows the user to securely reset their password on the LeanCloud site.
|
/// user account. This email allows the user to securely reset their password on the LeanCloud site.
|
||||||
|
@ -803,7 +747,7 @@ namespace LeanCloud {
|
||||||
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
|
/// <param name="mobilePhoneNumber">注册时使用的手机号</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) {
|
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) {
|
||||||
return AVUser.RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
|
return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -823,7 +767,7 @@ namespace LeanCloud {
|
||||||
/// <param name="validateToken">Validate token.</param>
|
/// <param name="validateToken">Validate token.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken) {
|
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken) {
|
||||||
return AVUser.RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None);
|
return RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -834,9 +778,7 @@ namespace LeanCloud {
|
||||||
/// <param name="cancellationToken">cancellationToken</param>
|
/// <param name="cancellationToken">cancellationToken</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
|
public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
Dictionary<string, object> strs = new Dictionary<string, object> {
|
||||||
Dictionary<string, object> strs = new Dictionary<string, object>()
|
|
||||||
{
|
|
||||||
{ "mobilePhoneNumber", mobilePhoneNumber },
|
{ "mobilePhoneNumber", mobilePhoneNumber },
|
||||||
};
|
};
|
||||||
if (String.IsNullOrEmpty(validateToken)) {
|
if (String.IsNullOrEmpty(validateToken)) {
|
||||||
|
@ -868,9 +810,7 @@ namespace LeanCloud {
|
||||||
/// <param name="cancellationToken">cancellationToken</param>
|
/// <param name="cancellationToken">cancellationToken</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode, CancellationToken cancellationToken) {
|
public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode, CancellationToken cancellationToken) {
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
Dictionary<string, object> strs = new Dictionary<string, object> {
|
||||||
Dictionary<string, object> strs = new Dictionary<string, object>()
|
|
||||||
{
|
|
||||||
{ "password", newPassword }
|
{ "password", newPassword }
|
||||||
};
|
};
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
|
@ -918,7 +858,6 @@ namespace LeanCloud {
|
||||||
/// <param name="cancellationToken">CancellationToken</param>
|
/// <param name="cancellationToken">CancellationToken</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
|
public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) {
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
|
||||||
Dictionary<string, object> strs = new Dictionary<string, object> {
|
Dictionary<string, object> strs = new Dictionary<string, object> {
|
||||||
{ "mobilePhoneNumber", mobilePhoneNumber }
|
{ "mobilePhoneNumber", mobilePhoneNumber }
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue