Compartilhar via


Usar dados de arquivo com registros de Anexo e Anotação

As tabelas Attachment (ActivityMimeAttachment) e Note (Annotation) contêm colunas de cadeia de caracteres especiais que armazenam dados de arquivo. Essas tabelas existiam antes das colunas de arquivo ou imagem, portanto, funcionam de forma diferente dessas tabelas.

  • Os dados do arquivo binário são armazenados como valores de cadeia de caracteres codificados em Base64 em colunas de cadeia de caracteres. Os anexos são armazenados na coluna ActivityMimeAttachment.Body e as anotações são armazenadas na coluna Annotation.DocumentBody.
  • Os dados do nome do arquivo são armazenados na FileName coluna.
  • Os dados de tipo MIME são armazenados na MimeType coluna.

PorqueFileName, MimeTypee ActivityMimeAttachment.BodyAnnotation.DocumentBody fazem parte dos dados do anexo ou registro de anotação, atualize essas três colunas junto com quaisquer outros valores.

Você pode obter e definir diretamente os valores das colunas activitymimeattachment.body e annotation.documentbody como cadeias de caracteres codificadas em Base64. Definir esses valores deve ser bom, desde que os arquivos não sejam muito grandes, por exemplo, abaixo de 4 MB. Por padrão, o tamanho máximo é de 5 MB. Você pode configurar essas colunas para aceitar arquivos de até 128 MB. Quando você aumentar o tamanho máximo do arquivo e trabalhar com arquivos maiores, use mensagens fornecidas para dividir os arquivos em partes menores ao carregar ou baixar arquivos. Para obter informações sobre como recuperar ou alterar os limites de tamanho do arquivo, consulte os limites de tamanho do arquivo.

Arquivos de anexo

Um anexo é um arquivo associado a uma atividade de email, diretamente ou por meio de um Modelo de Email. Você pode associar vários anexos à atividade ou modelo. Você pode reutilizar arquivos de anexo configurando o valor activitymimeattachment.attachmentid para se referir a outro anexo existente, em vez de definir as propriedades body, filename e mimetype.

Outras tabelas do Dataverse nomeadas anexo

Não confunda Attachment (ActivityMimeAttachment) com activityfileattachment, que dá suporte a arquivos associados à tabela Post .

No esquema do Dataverse, também há uma tabela pública com o nome Attachment, que é exposta na API Web como EntityType de anexo. Você pode consultar essa tabela e ela reflete os dados na ActivityMimeAttachment tabela. Mas ele não dá suporte a operações de criação, recuperação, atualização ou exclusão. Esta tabela não aparece no designer Power Apps.

Carregar arquivos de anexo

Use as mensagens InitializeAttachmentBlocksUpload, UploadBlock, e CommitAttachmentBlocksUpload para carregar arquivos grandes como anexos.

Importante

Você só pode usar essas mensagens para criar um novo anexo. Se você tentar usá-los para atualizar um anexo existente, receberá um erro de que o registro já existe.

O método estático UploadAttachment a seguir mostra como criar um anexo com um arquivo usando o InitializeAttachmentBlocksUploadRequest, UploadBlockRequeste CommitAttachmentBlocksUploadRequest classes para retornar um CommitAttachmentBlocksUploadResponse com ActivityMimeAttachmentId e FileSizeInBytes propriedades.

static CommitAttachmentBlocksUploadResponse UploadAttachment(
   IOrganizationService service,
   Entity attachment,
   FileInfo fileInfo,
   string fileMimeType = null)
{
   if (attachment.LogicalName != "activitymimeattachment")
   {
         throw new ArgumentException(
            "The attachment parameter must be an activitymimeattachment entity.",
            nameof(attachment));
   }

   // body value in activitymimeattachment not needed. Remove if found.
   if (attachment.Contains("body"))
   {
         attachment.Attributes.Remove("body");
   }

   // Try to get the mimetype if not provided.
   if (string.IsNullOrEmpty(fileMimeType))
   {
         var provider = new FileExtensionContentTypeProvider();

         if (!provider.TryGetContentType(fileInfo.Name, out fileMimeType))
         {
            fileMimeType = "application/octet-stream";
         }
   }
   // Don't overwrite mimetype value if it exists
   if (!attachment.Contains("mimetype"))
   {
         attachment["mimetype"] = fileMimeType;
   }

   // Initialize the upload
   InitializeAttachmentBlocksUploadRequest initializeRequest = new()
   {
         Target = attachment
   };

   var initializeResponse =
         (InitializeAttachmentBlocksUploadResponse)service.Execute(initializeRequest);

   string fileContinuationToken = initializeResponse.FileContinuationToken;

   // Capture blockids while uploading
   List<string> blockIds = new();

   using Stream uploadFileStream = fileInfo.OpenRead();

   int blockSize = 4 * 1024 * 1024; // 4 MB

   byte[] buffer = new byte[blockSize];
   int bytesRead = 0;

   long fileSize = fileInfo.Length;

   // The number of iterations that will be required:
   // int blocksCount = (int)Math.Ceiling(fileSize / (float)blockSize);
   int blockNumber = 0;

   // While there is unread data from the file
   while ((bytesRead = uploadFileStream.Read(buffer, 0, buffer.Length)) > 0)
   {
         // The file or final block may be smaller than 4MB
         if (bytesRead < buffer.Length)
         {
            Array.Resize(ref buffer, bytesRead);
         }

         blockNumber++;

         string blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));

         blockIds.Add(blockId);

         // Prepare the request
         UploadBlockRequest uploadBlockRequest = new()
         {
            BlockData = buffer,
            BlockId = blockId,
            FileContinuationToken = fileContinuationToken,
         };

         // Send the request
         service.Execute(uploadBlockRequest);
   }

   // Commit the upload
   CommitAttachmentBlocksUploadRequest commitRequest = new()
   {
         BlockList = blockIds.ToArray(),
         FileContinuationToken = fileContinuationToken,
         Target = attachment
   };
   
      return  (CommitAttachmentBlocksUploadResponse)service.Execute(commitRequest);

}

Para obter mais informações, consulte:

Observação

Este método de exemplo inclui alguma lógica para tentar obter o tipo MIME do arquivo usando o método FileExtensionContentTypeProvider.TryGetContentType(String, String) se ele não for fornecido. Se o tipo não for encontrado, ele será definido como application/octet-stream.

Baixar arquivos de anexo

Você pode baixar um arquivo de anexo em uma única operação usando a API Web ou em partes usando o SDK ou a API Web.

Baixar arquivos de anexo em uma única operação usando a API Web

Usando a API Web, você pode baixar um arquivo de anexo em uma única operação.

Ao contrário da recuperação de colunas de arquivo, esse método não fornece informações sobre o tamanho do arquivo, o nome do arquivo ou o tipo MIME.

Solicitação:

GET [Organization Uri]/api/data/v9.2/activitymimeattachments(<activitymimeattachmentid>)/body/$value HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json

Resposta:

HTTP/1.1 200 OK
OData-Version: 4.0
Content-Type: text/plain

<Base64 string content removed for brevity>

Para obter mais informações, consulte:

Baixar arquivos de anexo em partes

Para recuperar o arquivo em partes, use as seguintes mensagens com o SDK ou a API Web:

Message DESCRIÇÃO
InitializeAttachmentBlocksDownload Especifica o registro de nota do qual você deseja baixar um arquivo. Ele retorna o tamanho do arquivo em bytes e um token de continuação de arquivo que você pode usar para baixar o arquivo em blocos usando a DownloadBlock mensagem.
DownloadBlock Solicita o tamanho do bloco, o valor de diferença e o token de continuação do arquivo.

Depois de baixar todos os blocos, junte-se a eles para criar todo o arquivo baixado.

O método estático DownloadAttachment a seguir mostra como baixar um anexo usando o SDK com as classes InitializeAttachmentBlocksDownloadRequest e DownloadBlockRequest . Essa função retorna os byte[] dados e o nome do arquivo.

static (byte[] bytes, string fileName) DownloadAttachment(
   IOrganizationService service,
   EntityReference target)
{
   if (target.LogicalName != "activitymimeattachment")
   {
         throw new ArgumentException(
            "The target parameter must refer to an activitymimeattachment record.",
            nameof(target));
   }

   InitializeAttachmentBlocksDownloadRequest initializeRequest = new()
   {
         Target = target
   };

   var response =
         (InitializeAttachmentBlocksDownloadResponse)service.Execute(initializeRequest);

   string fileContinuationToken = response.FileContinuationToken;
   int fileSizeInBytes = response.FileSizeInBytes;
   string fileName = response.FileName;

   List<byte> fileBytes = new(fileSizeInBytes);

   long offset = 0;
   long blockSizeDownload = 4 * 1024 * 1024; // 4 MB

   // File size may be smaller than defined block size
   if (fileSizeInBytes < blockSizeDownload)
   {
         blockSizeDownload = fileSizeInBytes;
   }

   while (fileSizeInBytes > 0)
   {
         // Prepare the request
         DownloadBlockRequest downLoadBlockRequest = new()
         {
            BlockLength = blockSizeDownload,
            FileContinuationToken = fileContinuationToken,
            Offset = offset
         };

         // Send the request
         var downloadBlockResponse =
                  (DownloadBlockResponse)service.Execute(downLoadBlockRequest);

         // Add the block returned to the list
         fileBytes.AddRange(downloadBlockResponse.Data);

         // Subtract the amount downloaded,
         // which may make fileSizeInBytes < 0 and indicate
         // no further blocks to download
         fileSizeInBytes -= (int)blockSizeDownload;
         // Increment the offset to start at the beginning of the next block.
         offset += blockSizeDownload;
   }

   return (fileBytes.ToArray(), fileName);
}

Para obter mais informações, consulte:

Arquivos de anotação

Uma observação é um registro associado a uma linha de tabela que contém texto e pode ter um arquivo anexado. Somente tabelas com EntityMetadata.HasNotes definido como verdadeiro suportam notas associadas.

Carregar arquivos de anotação

Use as mensagens InitializeAnnotationBlocksUpload, UploadBlock e CommitAnnotationBlocksUpload para carregar arquivos para notas.

A anotação que você passa como o Target parâmetro para essas mensagens deve ter um annotationid valor. Esse valor é como você atualiza os registros de anotação existentes.

Normalmente, você permite que o Dataverse gere os valores de identificador exclusivos ao criar novos registros, mas não pode fazer isso com essas mensagens. Para criar uma nova anotação usando essas mensagens, gere um novo Guid valor para definir como o annotationid valor em vez de permitir que o Dataverse gere o valor.

O seguinte método estático UploadNote mostra como criar ou atualizar uma anotação com um arquivo usando as classes InitializeAnnotationBlocksUploadRequest, UploadBlockRequest e CommitAnnotationBlocksUploadRequest. Ele retorna um CommitAnnotationBlocksUploadResponse com AnnotationId e FileSizeInBytes propriedades.

static CommitAnnotationBlocksUploadResponse UploadNote(
   IOrganizationService service,
   Entity annotation,
   FileInfo fileInfo,
   string? fileMimeType = null)
{

   if (annotation.LogicalName != "annotation")
   {
         throw new ArgumentException(
            message: "The annotation parameter must be an annotation entity",
            paramName: nameof(annotation));
   }
   if (!annotation.Attributes.Contains("annotationid") || annotation.Id != Guid.Empty)
   {
         throw new ArgumentException(
            message: "The annotation parameter must include a valid annotationid value.",
            paramName: nameof(annotation));
   }

   // documentbody value in annotation not needed. Remove if found.
   if (annotation.Contains("documentbody"))
   {
         annotation.Attributes.Remove("documentbody");
   }

   // Try to get the mimetype if not provided.
   if (string.IsNullOrEmpty(fileMimeType))
   {
         var provider = new FileExtensionContentTypeProvider();

         if (!provider.TryGetContentType(fileInfo.Name, out fileMimeType))
         {
            fileMimeType = "application/octet-stream";
         }
   }
   // Don't override what might be included in the annotation.
   if (!annotation.Contains("mimetype")) {
         annotation["mimetype"] = fileMimeType;
   }
   
   // Initialize the upload
   InitializeAnnotationBlocksUploadRequest initializeRequest = new()
   {
         Target = annotation
   };

   var initializeResponse =
         (InitializeAnnotationBlocksUploadResponse)service.Execute(initializeRequest);

   string fileContinuationToken = initializeResponse.FileContinuationToken;

   // Capture blockids while uploading
   List<string> blockIds = new();

   using Stream uploadFileStream = fileInfo.OpenRead();

   int blockSize = 4 * 1024 * 1024; // 4 MB

   byte[] buffer = new byte[blockSize];
   int bytesRead = 0;

   long fileSize = fileInfo.Length;

   // The number of iterations that will be required:
   // int blocksCount = (int)Math.Ceiling(fileSize / (float)blockSize);
   int blockNumber = 0;

   // While there is unread data from the file
   while ((bytesRead = uploadFileStream.Read(buffer, 0, buffer.Length)) > 0)
   {
         // The file or final block may be smaller than 4MB
         if (bytesRead < buffer.Length)
         {
            Array.Resize(ref buffer, bytesRead);
         }

         blockNumber++;
         // Generates base64 string blockId values based on a Guid value so they are always the same length.
         string blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));

         blockIds.Add(blockId);

         // Prepare the request
         UploadBlockRequest uploadBlockRequest = new()
         {
            BlockData = buffer,
            BlockId = blockId,
            FileContinuationToken = fileContinuationToken,
         };

         // Send the request
         service.Execute(uploadBlockRequest);
   }

   // Commit the upload
   CommitAnnotationBlocksUploadRequest commitRequest = new()
   {
         BlockList = blockIds.ToArray(),
         FileContinuationToken = fileContinuationToken,
         Target = annotation
   };

      return  (CommitAnnotationBlocksUploadResponse)service.Execute(commitRequest);
}

Para obter mais informações, consulte:

Observação

Este método de exemplo inclui alguma lógica para tentar obter o tipo MIME do arquivo usando o método FileExtensionContentTypeProvider.TryGetContentType(String, String) se ele não for fornecido. Se o tipo não for encontrado, ele será definido como application/octet-stream.

Baixar arquivos de anotação

Você pode baixar um arquivo de Nota em uma única etapa usando a API Web, ou em partes usando o SDK ou a API Web.

Baixar arquivos de anotação em uma única etapa usando a API Web

Usando a API da Web, você pode baixar um arquivo de notas em uma única etapa:

Ao contrário da recuperação de colunas de arquivo, esse método não fornece informações sobre o tamanho do arquivo, o nome do arquivo ou o tipo MIME.

Solicitação:

GET [Organization Uri]/api/data/v9.2/annotations(<annotationid>)/documentbody/$value HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Accept: application/json

Resposta:

HTTP/1.1 200 OK
OData-Version: 4.0
Content-Type: text/plain

<Base64 string content removed for brevity>

Para obter mais informações, consulte:

Baixar arquivos de anotação em partes

Para recuperar o arquivo em partes, use as seguintes mensagens com o SDK ou a API Web:

Message DESCRIÇÃO
InitializeAnnotationBlocksDownload Especifica o registro de nota do qual você deseja baixar um arquivo. Ele retorna o tamanho do arquivo em bytes e um token de continuação de arquivo que você pode usar para baixar o arquivo em blocos usando a DownloadBlock mensagem.
DownloadBlock Solicita o tamanho do bloco, o valor de diferença e o token de continuação do arquivo.

Depois de baixar todos os blocos, junte-se a eles para criar todo o arquivo baixado.

O método estático DownloadNote a seguir mostra como baixar uma anotação usando o SDK com as classes InitializeAnnotationBlocksDownloadRequest e DownloadBlockRequest . Essa função retorna os byte[] dados e o nome do arquivo.

static (byte[] bytes, string fileName) DownloadNote(
    IOrganizationService service,
    EntityReference target)
{
if (target.LogicalName != "annotation")
{
      throw new ArgumentException("The target parameter must refer to an note record.", nameof(target));
}

InitializeAnnotationBlocksDownloadRequest initializeRequest = new()
{
      Target = target
};

var response =
      (InitializeAnnotationBlocksDownloadResponse)service.Execute(initializeRequest);

string fileContinuationToken = response.FileContinuationToken;
int fileSizeInBytes = response.FileSizeInBytes;
string fileName = response.FileName;

List<byte> fileBytes = new(fileSizeInBytes);

long offset = 0;
long blockSizeDownload = 4 * 1024 * 1024; // 4 MB

// File size may be smaller than defined block size
if (fileSizeInBytes < blockSizeDownload)
{
      blockSizeDownload = fileSizeInBytes;
}

while (fileSizeInBytes > 0)
{
      // Prepare the request
      DownloadBlockRequest downLoadBlockRequest = new()
      {
            BlockLength = blockSizeDownload,
            FileContinuationToken = fileContinuationToken,
            Offset = offset
      };

      // Send the request
      var downloadBlockResponse =
                  (DownloadBlockResponse)service.Execute(downLoadBlockRequest);

      // Add the block returned to the list
      fileBytes.AddRange(downloadBlockResponse.Data);

      // Subtract the amount downloaded,
      // which may make fileSizeInBytes < 0 and indicate
      // no further blocks to download
      fileSizeInBytes -= (int)blockSizeDownload;
      // Increment the offset to start at the beginning of the next block.
      offset += blockSizeDownload;
}

return (fileBytes.ToArray(), fileName);
}

Para obter mais informações, consulte:

Limites de tamanho de arquivo

A coluna Organization.MaxUploadFileSize especifica o tamanho máximo permitido de um arquivo em bytes para um anexo e uma anotação e outros tipos de dados, como arquivos de recursos da Web usados para aplicativos controlados por modelos. O limite máximo de tamanho do arquivo de upload se aplica ao tamanho do arquivo na codificação Base64. Uma codificação Base64 produz uma cadeia de caracteres maior que os dados de arquivo originais byte[] .

O tamanho padrão é de 5 MB (5.242.880 bytes) e o valor máximo é de 128 MB (131.072.000 bytes). Você pode definir esse valor nas configurações de email para o ambiente. Para obter mais informações, consulte Gerenciar configurações de email.

Se você tentar carregar um arquivo muito grande, receberá a seguinte mensagem de erro:

Nome: unManagedidsattachmentinvalidfilesize
Código: 0x80044a02
Número: -2147202558
Mensagem: Attachment file size is too big.

Recuperar o tamanho máximo do arquivo de upload

Você pode recuperar o tamanho máximo do arquivo de upload de algumas maneiras.

Use um método estático como o método a seguir GetMaxUploadFileSize para obter o valor.

public static int GetMaxUploadFileSize(IOrganizationService service) {

   QueryExpression query = new("organization") { 
         ColumnSet = new ColumnSet("maxuploadfilesize")
   };

   EntityCollection organizations = service.RetrieveMultiple(query);

   // There is only one row in organization table
   return (int)organizations.Entities.FirstOrDefault()["maxuploadfilesize"];
}

Para obter mais informações, consulte:

Alterar o tamanho máximo do arquivo de upload

Defina o organization.maxuploadfilesize valor de algumas maneiras.

Use um método estático como o seguinte SetMaxUploadFileSize para definir o tamanho máximo do arquivo de upload.

public static void SetMaxUploadFileSize(
    IOrganizationService service, 
    int maxUploadFileSizeInBytes)
{
   if (maxUploadFileSizeInBytes > 131072000 || maxUploadFileSizeInBytes < 1) {
         throw new ArgumentOutOfRangeException(nameof(maxUploadFileSizeInBytes), 
         "The maxUploadFileSizeInBytes parameter must be less than 131072000 bytes and greater than 0 bytes.");
   }

   QueryExpression query = new("organization")
   {
         ColumnSet = new ColumnSet("organizationid")
   };

   EntityCollection organizations = service.RetrieveMultiple(query);

   // There is only one row in organization table
   Entity organization = organizations.Entities.FirstOrDefault();
   organization["maxuploadfilesize"] = maxUploadFileSizeInBytes;

   service.Update(organization);
}

Para obter mais informações, consulte:

Consulte também

Visão geral de arquivos e imagens
Sample: operações de arquivo com anexos e anotações usando o SDK do Dataverse para .NET
Exemplo: operações de arquivo de anexo e anotação usando a API Web do Dataverse