FarmMapsApiClient/FarmmapsApi/Services/FarmmapsUploader.cs

125 lines
4.5 KiB
C#

using System;
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, string path)
: 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;
var twoMB = 2 * 0x100000;
ChunkSize = twoMB;
body.ChunkSize = ChunkSize;
Body = body;
Path = path;
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 builder = new RequestBuilder()
{
BaseUri = HttpClient.BaseAddress,
Path = Path,
Method = HttpMethod,
};
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;
}
}
}