combine projects into one
This commit is contained in:
283
Gallery.Share/Util/NetHelper.cs
Normal file
283
Gallery.Share/Util/NetHelper.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace Gallery.Util
|
||||
{
|
||||
public static class NetHelper
|
||||
{
|
||||
public static bool NetworkAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return Connectivity.NetworkAccess == NetworkAccess.Internet
|
||||
|| Connectivity.NetworkAccess == NetworkAccess.ConstrainedInternet;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<(T result, string error)> RequestObject<T>(string url,
|
||||
string referer = null,
|
||||
HttpContent post = null,
|
||||
Action<HttpRequestHeaders> headerHandler = null,
|
||||
Func<string, string> contentHandler = null,
|
||||
Func<string, T> @return = null)
|
||||
{
|
||||
var response = await Request(url, headers =>
|
||||
{
|
||||
if (referer != null)
|
||||
{
|
||||
headers.Referrer = new Uri(referer);
|
||||
}
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
headerHandler?.Invoke(headers);
|
||||
}, post);
|
||||
if (response == null)
|
||||
{
|
||||
return (default, "response is null");
|
||||
}
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Log.Print($"http failed with code: {(int)response.StatusCode} - {response.StatusCode}");
|
||||
return (default, response.StatusCode.ToString());
|
||||
}
|
||||
string content;
|
||||
using (response)
|
||||
{
|
||||
try
|
||||
{
|
||||
content = await response.Content.ReadAsStringAsync();
|
||||
if (contentHandler != null)
|
||||
{
|
||||
content = contentHandler(content);
|
||||
}
|
||||
if (@return != null)
|
||||
{
|
||||
return (@return(content), null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("stream.load", $"failed to read stream, error: {ex.Message}");
|
||||
return (default, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (content == null)
|
||||
{
|
||||
content = string.Empty;
|
||||
}
|
||||
try
|
||||
{
|
||||
var result = JsonConvert.DeserializeObject<T>(content);
|
||||
return (result, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var memo = content.Length < 20 ? content : content[0..20] + "...";
|
||||
Log.Error("content.deserialize", $"failed to parse JSON object, content: {memo}, error: {ex.Message}");
|
||||
return (default, content);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> DownloadImage(string url, string working, string folder)
|
||||
{
|
||||
try
|
||||
{
|
||||
var directory = Path.Combine(working, folder);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
var file = Path.Combine(directory, Path.GetFileName(url));
|
||||
var response = await Request(url, headers =>
|
||||
{
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
headers.Add("Accept", Config.AcceptImage);
|
||||
});
|
||||
if (response == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
using (response)
|
||||
using (var fs = File.OpenWrite(file))
|
||||
{
|
||||
await response.Content.CopyToAsync(fs);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("image.download", ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> DownloadImageAsync(string url, string id, string working, string folder)
|
||||
{
|
||||
try
|
||||
{
|
||||
var directory = Path.Combine(working, folder);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
var file = Path.Combine(directory, Path.GetFileName(url));
|
||||
var proxy = Config.Proxy;
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
UseCookies = false
|
||||
};
|
||||
if (proxy != null)
|
||||
{
|
||||
handler.Proxy = proxy;
|
||||
handler.UseProxy = true;
|
||||
}
|
||||
var client = new HttpClient(handler, true)
|
||||
{
|
||||
Timeout = Config.Timeout
|
||||
};
|
||||
long size;
|
||||
DateTimeOffset lastModified;
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Head, url))
|
||||
{
|
||||
var headers = request.Headers;
|
||||
headers.Add("Accept", Config.AcceptImage);
|
||||
headers.Add("Accept-Language", Config.AcceptLanguage);
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
size = response.Content.Headers.ContentLength.Value;
|
||||
lastModified = response.Content.Headers.LastModified.Value;
|
||||
#if DEBUG
|
||||
Log.Print($"content length: {size:n0} bytes, last modified: {lastModified}");
|
||||
#endif
|
||||
}
|
||||
|
||||
// segments
|
||||
const int SIZE = 150000;
|
||||
var list = new List<(long from, long to)>();
|
||||
for (long i = 0; i < size; i += SIZE)
|
||||
{
|
||||
long to;
|
||||
if (i + SIZE >= size)
|
||||
{
|
||||
to = size - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
to = i + SIZE - 1;
|
||||
}
|
||||
list.Add((i, to));
|
||||
}
|
||||
|
||||
var data = new byte[size];
|
||||
var task = new TaskCompletionSource<string>();
|
||||
|
||||
ParallelTask.Start($"download.async.{id}", 0, list.Count, 2, i =>
|
||||
{
|
||||
var (from, to) = list[i];
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
|
||||
{
|
||||
var headers = request.Headers;
|
||||
headers.Add("Accept", Config.AcceptImage);
|
||||
headers.Add("Accept-Language", Config.AcceptLanguage);
|
||||
headers.Add("Accept-Encoding", "identity");
|
||||
headers.IfRange = new RangeConditionHeaderValue(lastModified);
|
||||
headers.Range = new RangeHeaderValue(from, to);
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
using var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
|
||||
using var ms = new MemoryStream(data, (int)from, (int)(to - from + 1));
|
||||
response.Content.CopyToAsync(ms).Wait();
|
||||
#if DEBUG
|
||||
Log.Print($"downloaded range: from({from:n0}) to ({to:n0})");
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
},
|
||||
complete: o =>
|
||||
{
|
||||
using (var fs = File.OpenWrite(file))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
task.SetResult(file);
|
||||
});
|
||||
|
||||
return await task.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("image.download.async", $"failed to download image, error: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseMessage> Request(string url, Action<HttpRequestHeaders> headerHandler, HttpContent post = null)
|
||||
{
|
||||
#if DEBUG
|
||||
var method = post == null ? "GET" : "POST";
|
||||
Log.Print($"{method}: {url}");
|
||||
#endif
|
||||
var uri = new Uri(url);
|
||||
var proxy = Config.Proxy;
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate,
|
||||
UseCookies = false
|
||||
};
|
||||
if (proxy != null)
|
||||
{
|
||||
handler.Proxy = proxy;
|
||||
handler.UseProxy = true;
|
||||
}
|
||||
var client = new HttpClient(handler, true)
|
||||
{
|
||||
BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}:{uri.Port}"),
|
||||
Timeout = Config.Timeout
|
||||
};
|
||||
return await TryCount(() =>
|
||||
{
|
||||
using var request = new HttpRequestMessage(post == null ? HttpMethod.Get : HttpMethod.Post, uri.PathAndQuery);
|
||||
var headers = request.Headers;
|
||||
headerHandler?.Invoke(headers);
|
||||
headers.Add("Accept-Language", Config.AcceptLanguage);
|
||||
if (post != null)
|
||||
{
|
||||
request.Content = post;
|
||||
}
|
||||
return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
});
|
||||
}
|
||||
|
||||
private static T TryCount<T>(Func<T> func, int tryCount = 2)
|
||||
{
|
||||
int tries = 0;
|
||||
while (tries < tryCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
return func();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tries++;
|
||||
Thread.Sleep(1000);
|
||||
Log.Error("try.do", $"tries: {tries}, error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user