FarmMapsApiClient/FarmmapsApi/Services/FarmmapsUploader.cs

160 lines
6.1 KiB
C#

using System;
using System.Collections;
using System.IO;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FarmmapsApi.Models;
using Google.Apis.Http;
using Google.Apis.Json;
using Google.Apis.Requests;
using Google.Apis.Upload;
using Google.Apis.Util;
using Newtonsoft.Json;
namespace FarmmapsApi.Services
{
public class FarmmapsUploader : ResumableUpload
{
/// <summary>Payload description headers, describing the content itself.</summary>
private const string PayloadContentTypeHeader = "X-Upload-Content-Type";
/// <summary>Payload description headers, describing the content itself.</summary>
private const string PayloadContentLengthHeader = "X-Upload-Content-Length";
private const int UnknownSize = -1;
/// <summary>Gets or sets the service.</summary>
private HttpClient HttpClient { get; }
/// <summary>
/// Gets or sets the path of the method (combined with
/// <see cref="Google.Apis.Services.IClientService.BaseUri"/>) to produce
/// absolute Uri.
/// </summary>
private string Path { get; }
/// <summary>Gets or sets the HTTP method of this upload (used to initialize the upload).</summary>
private string HttpMethod { get; }
/// <summary>Gets or sets the stream's Content-Type.</summary>
private string ContentType { get; }
/// <summary>Gets or sets the body of this request.</summary>
private FileRequest Body { get; }
private long _streamLength;
/// <summary>
/// Create a resumable upload instance with the required parameters.
/// </summary>
/// <param name="contentStream">The stream containing the content to upload.</param>
/// <remarks>
/// Caller is responsible for maintaining the <paramref name="contentStream"/> open until the upload is
/// completed.
/// Caller is responsible for closing the <paramref name="contentStream"/>.
/// </remarks>
public FarmmapsUploader(ConfigurableHttpClient httpClient, Stream contentStream, FileRequest body, string contentType)
: base(contentStream,
new ResumableUploadOptions
{
HttpClient = httpClient,
Serializer = new NewtonsoftJsonSerializer(),
ServiceName = "FarmMaps"
})
{
httpClient.ThrowIfNull(nameof(httpClient));
contentStream.ThrowIfNull(nameof(contentStream));
_streamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
Body = body;
Path = "api/v1/file";
HttpClient = httpClient;
HttpMethod = HttpConsts.Post;
ContentType = contentType;
}
/// <inheritdoc/>
public override async Task<Uri> InitiateSessionAsync(CancellationToken cancellationToken = default)
{
HttpRequestMessage request = CreateInitializeRequest();
Options?.ModifySessionInitiationRequest?.Invoke(request);
var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
throw await ExceptionForResponseAsync(response).ConfigureAwait(false);
}
return response.Headers.Location;
}
/// <summary>Creates a request to initialize a request.</summary>
private HttpRequestMessage CreateInitializeRequest()
{
var baseAddres = HttpClient.BaseAddress;
var uri = new Uri($"{baseAddres.Scheme}://{baseAddres.Host}:{baseAddres.Port}");
var builder = new RequestBuilder()
{
BaseUri = uri,
Path = Path,
Method = HttpMethod,
};
SetAllPropertyValues(builder);
HttpRequestMessage request = builder.CreateRequest();
if (ContentType != null)
{
request.Headers.Add(PayloadContentTypeHeader, ContentType);
}
// if the length is unknown at the time of this request, omit "X-Upload-Content-Length" header
if (ContentStream.CanSeek)
{
request.Headers.Add(PayloadContentLengthHeader, _streamLength.ToString());
}
var jsonText = JsonConvert.SerializeObject(Body);
request.Content = new StringContent(jsonText, Encoding.UTF8, MediaTypeNames.Application.Json);
return request;
}
/// <summary>
/// Reflectively enumerate the properties of this object looking for all properties containing the
/// RequestParameterAttribute and copy their values into the request builder.
/// </summary>
private void SetAllPropertyValues(RequestBuilder requestBuilder)
{
Type myType = this.GetType();
var properties = myType.GetProperties();
foreach (var property in properties)
{
var attribute = property.GetCustomAttribute<RequestParameterAttribute>();
if (attribute != null)
{
string name = attribute.Name ?? property.Name.ToLower();
object value = property.GetValue(this, null);
if (value != null)
{
if (!(value is string) && value is IEnumerable valueAsEnumerable)
{
foreach (var elem in valueAsEnumerable)
{
requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(elem));
}
}
else
{
// Otherwise just convert it to a string.
requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(value));
}
}
}
}
}
}
}