forked from FarmMaps/FarmMapsApiClient
Initial resumable file upload.
Other changes.
This commit is contained in:
160
FarmmapsApi/Services/FarmmapsUploader.cs
Normal file
160
FarmmapsApi/Services/FarmmapsUploader.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user