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); } }