Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The AL language in Business Central comes with support for streaming of data. In this article, we introduce what streams are and how they can be used in your app.
What is a stream?
A stream is an abstraction of a sequence of bytes, such as a file, an input/output device, an inter-process communication pipe, or a TCP/IP socket. The stream data types in AL provide a generic view of these different types of input and output, and isolate the developer from the specific details of the operating system and the underlying devices.
Streams involve three fundamental operations:
- You can read from streams
Reading is the transfer of data from a stream into a data structure, such as a Text or Blob. - You can write to streams
Writing is the transfer of data from a data structure such as Text or Blob into a stream. - Streams can support seeking
Seeking refers to querying and modifying the current position within a stream. Seek capability depends on the kind of backing store a stream has. For example, network streams have no unified concept of a current position, and therefore typically don't support seeking.
A stream is an object used for transferring data, so no actual data is stored in the stream. When working with streams in AL, you need three things.
- A data source such as a file, a Blob, or an HTTP request
- A stream object connected to the data source. The stream has a direction of reading or writing data to/from the data source.
- An object in the AL runtime acting as the consumer or the emitter of the data.
How are streams implemented in AL?
The AL stream object/methods are wrappers over corresponding .NET stream concepts. In C#, you only have one object called Stream. The direction (read/write) is determined by using the Read/Write methods as illustrated in this C# example:
// how to write the content of one stream into another stream in C#
static void CopyStream(Stream input, Stream output){
byte[] buffer = new byte[0x1000];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
output.Write(buffer, 0, read);
}
In AL, the direction of the data flow is instead encoded in the two data types InStream
and OutStream
and you need to use an AL object to either consume (read) data from a data source using a stream, or emit (write) data to a data source using a stream.
The AL runtime also has a built-in method for copying a stream, see System.CopyStream(OutStream: OutStream, InStream: InStream [, BytesToRead: Integer]).
Note
The CopyStream method stems from the time of the C/AL programming language, which was inspired from the Pascal programming language. In Pascal it's common for procedures to follow the direction of assignments, for example, variable := value (like dest := source). This is the reason why parameters in CopyStream are ordered the way they are.
Reading data with the InStream data type
The InStream datatype provides a generic stream object with methods that you can use to read from streams. The data type also provides methods to change the position in the stream and a way to get the size of the data source object without the need to read all of the data.
An instance of the InStream datatype must be attached to a data source to work, otherwise you get a runtime error stating that InStream variable not initialized..
trigger OnAction()
var
vInStr: InStream;
begin
// this will trigger a runtime error
Message(Format(vInStr.Length()));
end;
The following example illustrates how to read the content from a media field in the database using an InStream object.
procedure ReadTextFromMediaResource(MediaResourcesCode: Code[50]) MediaText: Text
var
MediaResources: Record "Media Resources";
TextInStream: InStream;
begin
if not MediaResources.Get(MediaResourcesCode) then
exit;
MediaResources.CalcFields(Blob);
// After this call, the TextInStream is ready to stream data from the blob field
MediaResources.Blob.CreateInStream(TextInStream, TextEncoding::UTF8);
TextInStream.Read(MediaText);
end;
For more information, see InStream datatype (reference documentation).
Writing data with the OutStream datatype
The OutStream datatype provides a generic stream object with methods that you can use to write to resources using streams.
An instance of the OutStream datatype must be attached to a data source to work, else you'll get a runtime error stating that InStream variable not initialized..
The following example illustrates how to read the content from an uploaded file using an InStream object and then copy the stream content into a blob field using an OutStream object.
procedure InsertBLOBFromFileUpload()
var
FromFilter: Text;
File: File;
FileInStream: InStream;
BLOBOutStream: OutStream;
MyTable : Record "Some table";
begin
FromFilter := 'All Files (*.*)|*.*';
UploadIntoStream(FromFilter, FileInStream);
MyTable.Init();
MyTable.Blob.CreateOutStream(BLOBOutStream);
CopyStream(BLOBOutStream, FileInStream);
MyTable.Insert(true);
end;
For more information, see OutStream datatype (reference documentation)
Why use streams?
- Memory efficiency: Streams allow you to work with large amounts of data without loading it all into memory at once. If you can stream data instead of storing it in a variable, then the Business Central server has less large objects to handle when the operating system does garbage collection, and this in turn will improve the general performance of the system.
- Performance efficiency: Streams allow you to work with data as it becomes available, rather than waiting for all of it to arrive.
- When working with Business Central online - you can't use the file system directly. Streams provide a way to work with data without having to store it in a file.
Examples of stream support
There are many AL data types or objects that you can use to consume data from streams or emit data to streams. In this table, you can some examples on how to do this. The list isn't exhaustive.
Data source | Consume data with InStream | Emit data with OutStream |
---|---|---|
Web service | Read data from stream: HttpContent.ReadAs Send data using stream: HttpContent.WriteFrom |
|
Local file (only for on-premises) | File.CreateInStream | File.CreateOutStream |
File Upload/download | DownloadFromStream | UploadIntoStream |
XML document | XmlDocument.ReadFrom | XmlDocument.WriteTo |
JSON document | JsonObject.ReadFrom | JsonObject.WriteTo(OutStream) |
Media/Mediaset | Media.ImportStream MediaSet.ImportStream |
Media.ExportStream |
Excel (in-memory buffer) | OpenBookStream | SaveToStream |
CSV (in-memory buffer) | LoadDataFromStream | |
Blob field in the database | Temp Blob codeunit | Temp Blob codeunit |
Tip
When streaming binary data, you might need to do a Base64 encoding to make it available as a text stream. The System Application has a module for this, see Codeunit "Base64 Convert".
Streaming text data
When streaming text data, you need to be aware of encodings, which typically is controlled by the TextEncoding Option Type. For more information, Text encoding
Also, you might also want to learn more about the semantics of line endings and zero byte terminators. For more information, see Write, WriteText, Read, and ReadText method behavior for line endings and zero terminators
Related information
InStream datatype (AL reference documentation)
OutStream datatype (AL reference documentation)
System.CopyStream method (AL reference documentation)
Codeunit "Base64 Convert" (System Application reference documentation)
TextEncoding Option Type
Text encoding
Write, WriteText, Read, and ReadText method behavior for line Endings and Zero Terminators