using System.Text;
using Google.Protobuf;
using static MFiles.Grpc.Samples.MFilesValues;
namespace MFiles.Grpc.Samples.Recipes;
///
/// Recipe "create-document": upload a file and create a single-file document from it.
/// Arguments: [--keep] to leave the document in the vault (it is deleted otherwise).
///
public static class CreateDocument
{
/// Bytes per UploadTempFile call: a multiple of 4096, well below the message limit.
private const int BlockSize = 4 * 1024 * 1024;
public static async Task RunAsync(MFilesSettings settings, string[] args)
{
await using var connection = await MFilesConnection.ConnectAsync(settings, RecipeHelpers.ClientName);
string title = $"{RecipeHelpers.TestPrefix} Created by the C# sample {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}";
byte[] content = Encoding.UTF8.GetBytes($"{title}\r\nHello from the M-Files gRPC API.\r\n");
var objects = new IRPCObjectOperations.IRPCObjectOperationsClient(connection.CallInvoker);
// [START upload-file]
// Upload the content to a temporary file on the server, in blocks. The first call
// (upload_id 0) starts the upload; the next ones continue it.
int uploadId = 0;
for (int offset = 0; offset == 0 || offset < content.Length; offset += BlockSize)
{
var block = await objects.UploadTempFileAsync(new UploadTempFileRequest
{
UploadId = uploadId,
TotalSizeInBytes = content.Length,
Offset = offset,
Data = ByteString.CopyFrom(content, offset, Math.Min(BlockSize, content.Length - offset)),
});
uploadId = block.UploadId;
}
await objects.UploadTempFileCommitAsync(new UploadTempFileCommitRequest
{
UploadId = uploadId,
LogicalSize = content.Length,
});
// [END upload-file]
// [START create-document]
var response = await objects.AddObjectWithFilesAsync(new AddObjectWithFilesRequest
{
ObjectTypeId = 0, // Document
Properties =
{
Property(0, Text(title)), // Name or title
Property(100, Lookup(1, 1)), // Class: value list 1, item 1 "Other Document"
Property(22, Boolean(true)), // Single file
},
// Always send an ACL: without one, the new document has no permissions and not even
// its creator can see it. Named ACL -1 is "Full control for all internal users".
Acl = new AccessControlList
{
CustomComponent = new AccessControlListComponent { HasNamedAcl = true, NamedAclId = -1 },
},
Files = new ObjFileSource
{
Type = ObjFileSourceType.UploadedFile,
Data = new ObjFileSourceUnion
{
UploadedFile = new ObjFileSourceUnion.Types.UploadedFileData
{
UploadId = uploadId,
Title = title, // a single-file document's file takes the document's title
Extension = "txt",
Size = content.Length,
},
},
},
CheckIn = true,
});
var created = response.CreatedObject.ObjectVersion;
Console.WriteLine($"Created {RecipeHelpers.Describe(created)}");
// [END create-document]
foreach (var file in created.VersionInfo.Files)
Console.WriteLine($" File: {file.Title}.{file.Extension}, {file.Size} bytes");
if (!RecipeHelpers.Keep(args))
await RecipeHelpers.DeleteAsync(connection, created.ObjectInfo.ObjId);
}
}