Attach files programmatically using x++

Hi People,

I’m trying to attach files via a web api call (through postman). The file comes in saves the to the docuvalue and docuref tables and expected. but the “Preview” isnt showing. Kindly help…

Here’s my code

public void insert()
{
DocuRef docuRefTable;
DocuValue docuValueTable;

str firstFilePath, firstFileTempPath, firstFileBase64, firstFileName, firstFileExtension;

int64 _refRecId;
int _refTableId = 16462;
BinData binData;
System.IO.FileStream FileStream ;
System.Byte byte;
FileIoPermission perm;
String20 returnMsg;
str strUTF8Input;
FileIOPermission permission = new FileIOPermission(’’, ‘r’);
permission.assert();

firstFileBase64 = this.FirstFileBase64;
firstFileName = this.FirstFileName;
firstFileExtension = this.FirstFileExtension;

_refRecId = str2Int64(this.PointSourceAppRegRecId);
firstFilePath = System.IO.Path::GetTempPath(); //Get temp path

//str64 = System.Convert::ToBase64String(_byteArray);//Convert input Byte array in base64 string

firstFileTempPath = firstFilePath + firstFileName + ‘.’ + firstFileExtension;

if (_refRecId > 0)
{

ttsBegin;
docuValueTable.clear();
binData = new binData();
docuValueTable.File = BinData::loadFromBase64(firstFileBase64);
docuValueTable.Name = firstFileName;
docuValueTable.FileName = firstFileName;
docuValueTable.FileType = firstFileExtension;
docuValueTable.OriginalFileName = firstFileName + “.” + firstFileExtension;
docuValueTable.Path = firstFileTempPath;
docuValueTable.insert(); // insert into DB

if (docuValueTable.RecId)
{
docuRefTable.clear();
docuRefTable.RefRecId = _refRecid;
docuRefTable.RefTableId = _refTableId;
docuRefTable.RefCompanyId = curext();
docuRefTable.Name = firstFileName;
docuRefTable.TypeId = ‘File’;
docuRefTable.ValueRecId = docuValueTable.RecId;

docuRefTable.insert();
returnMsg = “Uploaded successfully”;
}
ttsCommit;
}
else
{

returnMsg = “Record not exit for the given Id”;
}

//return returnMsg;

super();
}

Does preview work for your if you upload files through GUI?

yes it does

i uploaded this from the GUI

when i browsed the DocuValue table for this entry, the AccessInformation column contains something like this "https://somedomain.blob.core.windows.net/documents/962723BE-3DA0-48CF-9DA4-DE3F2EAA759B" while the StorageProviderId column reads "Azure"

but for the attachments via postman, AccessInformation column is blank and StorageProviderId is blank as well

[mention:64fb33d4ab384f5ebca3050a0d9ca94f:e9ed411860ed4f2ba0265705b8793d05] you there?

And does the file attached by your code works correctly if you download it?

no it doesnt download

All right, so it’s not about the preview but your upload doesn’t work at all. So let’s look at your code.

I see you’re trying to put the file content to the table, which is wrong, because files are usually stored somewhere else. You also ignore the fact that users can choose where files will be stored, therefore you mustn’t hard-code such things.

The URL https://somedomain.blob.core.windows.net suggests that your system is currently configured to store files in Azure Blob Storage. Your code doesn’t upload anything there at all.

Please stop trying to put data into these tables directly and use DocuActionFile::attachFile() instead.

thanks Martin i thought as much as well about the upload to Azure.

can i get a snippet or an article on how i can use DocuActionFile::attachFile() ?

Sure - right-click the method and choose Find references to find places where it’s already used.

One example is DocumentManagement::attachFile(), which you may also use instead of DocuActionFile.

thanks Martin, i found this

DocumentManagement::attachFile(tableId, _refRecId, ‘dataAreaId’, DocuValueType::Others, _file, _fileName, _fileContentType, _attachmentName, _notes);

the challenge i have now is with the _file argument. It’s of type System.IO.Stream, but i’m passing my file across as a base64 encoded string. I was able to convert to Bindata with

BinData b = BinData::loadFromBase64(firstFileBase64);

BinData is of type container. So how do i go about transforming “container” to “Stream”. tried my hands on some methods. none worked.

Or could you suggest me a better way instead of base64 encoded string

I would do something like this:

System.Byte[] data = System.Convert::FromBase64String(firstFileBase64);
System.IO.Stream stream = new System.IO.MemoryStream(data);

Thanks Martin. The attachFile method requires an argument of type DocTypeId.

I don’t know what that means exactly and what to pass as value for that…

i snooped into the x++ codebase and found a class DocuActionFile and i found this method createFileAttachment, copied it to my code and tried to tweak it.

here’s the signature

///


/// Creates a new file attachment for the provided DocuRef record.
///

/// The DocuRef record that the file should be tied to.
/// The name of the file to attach.
/// The MIME type of the file to attach.
/// The contents of the file to attach.
/// The DocuValue record that was created for the new attachment.

protected DocuValue createFileAttachment(DocuRef _ref, int64 _refRecId, int64 _refTableId, str _fileName, str _fileExtension, System.IO.Stream _fileContents)

{

…my code

}

ive substitued _fileExtension for _fileContentType

file gets uploaded this time but the preview doesnt show but the file downloads as an extension-less file. so if add the correct extension to it and try to open, it opens.

I think the issue is with the MIME type.

Any suggestions?

docValue.Name = fileNameWithoutExtension;
docValue.FileId = newGuid();
docValue.FileName = fileNameWithoutExtension;
docValue.FileType = _fileExtension;
docValue.OriginalFileName = _fileName + “.” + _fileExtension;
docValue.Type = DocuValueType::Others;

DocuActionFile.createFileAttachment() is a protected method called from the public method attachFile(), which I showed you before. You can’t call protected method from outside, therefore you can’t use createFileAttachment() instead of attachFile(). Also, you would bypass validations implemented in attachFile().

File extensions and MIME types are different things, therefore trying to use a file extension as a MIME type is an obvious bug. The MIME type for .xlsx is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, I think. Try using System.Web.MimeMapping::GetMimeMapping() to get MIME types from filenames.

thanks Martin, really appreciate this…

ill just call the DocumentManagement::attachFile() method instead then but then DocuTypeId argument, what am i to pass?

i think i can take care of the MIME type since i’ll be restricting users from the front end to some specific file extensions they can pick for upload

I meant attachFile() of DocuActionFile in my last reply.

Regarding DocuTypeId EDT, open it in designer and look at its Table reference node. You’ll see that it’s a reference to DocuType table, namely to its TypeId field. Document types can be seen and managed here: Organization administration > Document management > Document types. You can find this information by using Find references.