lessto CloudToFile

Demo of Business Central 365 wave 2 (15) where data is transferred to and from a FTP site. A simple cloud to file function.

Code for Business Central 365 Spring Edition (14)
Simpel demo of the four FTP commands
The full wordpress code from the demo.

The code works both in local installation, server onprem, docker and cloud version.

The first page extenstions are for demo purpose, use the code to make your own AL code.

The codeunit is what makes the magic happens, so do not make changes.

Feel free to use the code if you have projects with http request, JSON conversion and BASE64.

The keypoints is to change the APIKEY and ftp information, this can be done in the AL code. In customer code it should be saved in a table.

The next keypoint is to make the call to the codeunit.

lesstoFTPmanagement.lesstoFTPsend(apikey, ftpuser, ftppassword, ftpserver, ftpdir, ftpfilename, base64, errorno, errortext, status, serverfile);

You can make this call in pages and codeunits and batchjobs.

Here is the code. You could make the codeunit as one extension and the page extensions as another. After installation the actions are on the customer list->processing.

pageextension 50000 lesstoFTPhttps extends "Customer List"
{
actions
{
addlast(Processing)
{

Action(FTPsendFile)
{
ApplicationArea = Basic, Suite;
image = GetEntries;
Caption = 'FTPsendfile';
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
trigger OnAction();
var
apikey: Text[100];
ftpuser: Text[20];
ftppassword: Text[20];
ftpserver: Text[20];
ftpdir: Text[100];
ftpfilename: Text[50];
base64: text;
lesstoFTPmanagement: codeunit "lesstoFTPmanagement";

Customer: Record Customer;
CodeBlob: codeunit "Temp Blob";
DataString: Text;
FileOutStream: OutStream;
FileInStream: InStream;
CRLF: text[2];
Errorno: Text[2];
Errortext: Text[100];
Status: Text[100];
Serverfile: Text[200];
Convert: Codeunit "Base64 Convert";
begin

apikey := '<Your API key from lessto Cloud To File>';
ftpuser := '<FTP server user name>';
ftppassword := '<FTP server user password>';
ftpserver := '<FTP server name>';
ftpdir := '<FTP server directory>';
ftpfilename := 'testcsvfile.csv'; // filename in this demo

CRLF := ' ';
CRLF[1] := 13;
CRLF[2] := 10;

if customer.findfirst then
repeat
datastring := datastring + '"' + customer."No." + '","' + customer.Name + '"' + CRLF;
until customer.next = 0;

base64 := convert.ToBase64(datastring);

lesstoFTPmanagement.lesstoFTPsend(apikey, ftpuser, ftppassword, ftpserver, ftpdir, ftpfilename, base64, errorno, errortext, status, serverfile);

if errorno <> '' then begin
message('Errorno = ' + errorno + ' Errortext = ' + errortext);
exit;
end;

message(status);

end;
}

Action(FTPgetdir)
{
ApplicationArea = Basic, Suite;
image = GetEntries;
Caption = 'FTPgetdir';
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
trigger OnAction();
var
apikey: Text[100];
ftpuser: Text[20];
ftppassword: Text[20];
ftpserver: Text[20];
ftpdir: Text[100];
lesstoFTPmanagement: codeunit "lesstoFTPmanagement";
Errorno: Text[2];
Errortext: Text[100];
Status: Text[100];
dirlist: array[1000] of text[100];
i: integer;
direntry: text;
CRLF: text[2];
Serverfile: Text[200];
begin

apikey := '<Your API key from lessto Cloud To File>';
ftpuser := '<FTP server user name>';
ftppassword := '<FTP server user password>';
ftpserver := '<FTP server name>';
ftpdir := '<FTP server directory>';

CRLF := ' ';
CRLF[1] := 13;
CRLF[2] := 10;

lesstoFTPmanagement.lesstoFTPgetdir(apikey, ftpuser, ftppassword, ftpserver, ftpdir, errorno, errortext, status, dirlist, serverfile);

if errorno <> '' then begin
message('Errorno = ' + errorno + ' Errortext = ' + errortext);
exit;
end;

for i := 1 to arraylen(dirlist) do begin
if dirlist[i] <> '' then
direntry := direntry + dirlist[i] + CRLF;
end;

message(direntry);

end;
}

Action(FTPgetfile)
{
ApplicationArea = Basic, Suite;
image = GetEntries;
Caption = 'FTPgetfile';
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
trigger OnAction();
var
apikey: Text[100];
ftpuser: Text[20];
ftppassword: Text[20];
ftpserver: Text[20];
ftpdir: Text[100];
ftpfilename: Text[100];
Textfile: Text;
lesstoFTPmanagement: codeunit "lesstoFTPmanagement";
Errorno: Text[2];
Errortext: Text[100];
Status: Text[100];
dirlist: array[1000] of text[100];
i: integer;
direntry: text;
CRLF: text[2];
Serverfile: Text[200];
begin

apikey := '<Your API key from lessto Cloud To File>';
ftpuser := '<FTP server user name>';
ftppassword := '<FTP server user password>';
ftpserver := '<FTP server name>';
ftpdir := '<FTP server directory>';
ftpfilename := 'testcsvfile.csv'; // filename in this demo

CRLF := ' ';
CRLF[1] := 13;
CRLF[2] := 10;

lesstoFTPmanagement.lesstoFTPget(apikey, ftpuser, ftppassword, ftpserver, ftpdir, ftpfilename, textfile, errorno, errortext, status, Serverfile);

if errorno <> '' then begin
message('Errorno = ' + errorno + ' Errortext = ' + errortext);
exit;
end;

message(textfile);

end;
}

Action(FTPdeletefile)
{
ApplicationArea = Basic, Suite;
image = GetEntries;
Caption = 'FTPdeletefile';
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
trigger OnAction();
var
apikey: Text[100];
ftpuser: Text[20];
ftppassword: Text[20];
ftpserver: Text[20];
ftpdir: Text[100];
ftpfilename: Text[100];
Textfile: Text;
lesstoFTPmanagement: codeunit "lesstoFTPmanagement";
Errorno: Text[2];
Errortext: Text[100];
Status: Text[100];
Serverfile: Text[200];

begin

apikey := '<Your API key from lessto Cloud To File>';
ftpuser := '<FTP server user name>';
ftppassword := '<FTP server user password>';
ftpserver := '<FTP server name>';
ftpdir := '<FTP server directory>';
ftpfilename := 'testcsvfile.csv'; // filename in this demo

lesstoFTPmanagement.lesstoFTPdelete(apikey, ftpuser, ftppassword, ftpserver, ftpdir, ftpfilename, errorno, errortext, status, serverfile);

if errorno <> '' then begin
message('Errorno = ' + errorno + ' Errortext = ' + errortext);
exit;
end;

message(status);

end;
}
}
}
}
codeunit 50000 "lesstoFTPmanagement"

{
trigger OnRun()
var

begin

end;

procedure lesstoFTPgetdir(apikey: Text[100]; ftpuser: Text[20]; ftppassword: Text[20]; ftpserver: Text[20]; ftpdir: Text[100]; var errorno: text[2]; var errortext: text[100]; var status: text[100]; var dirlist: array[1000] of text[100]; var serverfile: text[200]);

var

Codeblob: codeunit "Temp Blob";
JsonResArray: JsonArray;
JsonResToken: JsonToken;
JsonResObject: JsonObject;
httploadOutStream: OutStream;
httploadInStream: InStream;
Content: HttpContent;
ContentHeaders: HttpHeaders;
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
ResponseText: text;
PostString: Text;
i: Integer;
j: Integer;
maxi: integer;
Keystring: text;

begin

Content.GetHeaders(ContentHeaders);
ContentHeaders.Clear();
ContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
codeblob.CreateOutStream(httploadOutStream, TextEncoding::UTF8);

poststring := '&action=list' +
'&apikey=' + apikey +
'&user=' + ftpuser +
'&pass=' + ftppassword +
'&ftpserver=' + ftpserver +
'&ftpdir=' + ftpdir;

httploadOutStream.WriteText(poststring);

Codeblob.CreateInStream(httploadInStream, TextEncoding::UTF8);
Content.WriteFrom(httploadInStream);
Request.Content := Content;
Request.SetRequestUri('https://lessto.dk/cloudtofile/lessto-ftp-connect');
Request.Method := 'POST';

Client.Send(Request, Response);

Response.Content().ReadAs(ResponseText);

ResponseText := '[' + ResponseText + ']';
if not JsonResArray.ReadFrom((ResponseText)) then begin
errorno := '99';
errortext := 'Connection error';
exit;
end;

For i := 0 to jsonresarray.count - 1 do begin
jsonResarray.get(i, jsonrestoken);
JsonResObject := jsonrestoken.asobject;
end;

if jsonresobject.get('errorno', JsonResToken) then
errorno := jsonrestoken.asvalue.astext;

if jsonresobject.get('errortext', JsonResToken) then
errortext := jsonrestoken.asvalue.astext;

if jsonresobject.get('status', JsonResToken) then
status := jsonrestoken.asvalue.astext;

if jsonresobject.get('serverfile', JsonResToken) then
serverfile := jsonrestoken.asvalue.astext;

maxi := 1000;

j := 0;
For i := 0 to maxi do begin
Keystring := 'file' + format(i);
if jsonresobject.get(Keystring, JsonResToken) then begin
j := j + 1;
dirlist[j] := jsonrestoken.asvalue.astext;
end;
end;
end;

procedure lesstoFTPsend(apikey: Text[100]; ftpuser: Text[20]; ftppassword: Text[20]; ftpserver: Text[20]; ftpdir: Text[100]; ftpfilename: Text[50]; base64: text; var errorno: text[2]; var errortext: text[100]; var status: text[100]; var serverfile: text[200]);
var
Codeblob: Codeunit "Temp Blob";
JsonResArray: JsonArray;
JsonResToken: JsonToken;
JsonResObject: JsonObject;
httploadOutStream: OutStream;
httploadInStream: InStream;
Content: HttpContent;
ContentHeaders: HttpHeaders;
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
ResponseText: text;
PostString: Text;
i: Integer;

begin

Content.GetHeaders(ContentHeaders);
ContentHeaders.Clear();
ContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
Codeblob.CreateOutStream(httploadOutStream, TextEncoding::UTF8);

poststring := '&action=send' +
'&apikey=' + apikey +
'&user=' + ftpuser +
'&pass=' + ftppassword +
'&ftpserver=' + ftpserver +
'&ftpdir=' + ftpdir +
'&ftpfilename=' + ftpfilename +
'&base64=' + base64;

httploadOutStream.WriteText(poststring);

Codeblob.CreateInStream(httploadInStream, TextEncoding::UTF8);
Content.WriteFrom(httploadInStream);
Request.Content := Content;
Request.SetRequestUri('https://lessto.dk/cloudtofile/lessto-ftp-connect');
Request.Method := 'POST';

Client.Send(Request, Response);

Response.Content().ReadAs(ResponseText);

ResponseText := '[' + ResponseText + ']';
if not JsonResArray.ReadFrom((ResponseText)) then begin
errorno := '99';
errortext := 'Connection error';
exit;
end;

For i := 0 to jsonresarray.count - 1 do begin
jsonResarray.get(i, jsonrestoken);
JsonResObject := jsonrestoken.asobject;
end;

if jsonresobject.get('errorno', JsonResToken) then
errorno := jsonrestoken.asvalue.astext;

if jsonresobject.get('errortext', JsonResToken) then
errortext := jsonrestoken.asvalue.astext;

if jsonresobject.get('status', JsonResToken) then
status := jsonrestoken.asvalue.astext;

if jsonresobject.get('serverfile', JsonResToken) then
serverfile := jsonrestoken.asvalue.astext;

end;

procedure lesstoFTPget(apikey: Text[100]; ftpuser: Text[20]; ftppassword: Text[20]; ftpserver: Text[20]; ftpdir: Text[100]; ftpfilename: Text[50]; var textfile: text; var errorno: text[2]; var errortext: text[100]; var status: text[100]; var serverfile: text[200]);
var
CodeBlob: Codeunit "Temp Blob";
JsonResArray: JsonArray;
JsonResToken: JsonToken;
JsonResObject: JsonObject;
httploadOutStream: OutStream;
httploadInStream: InStream;
Content: HttpContent;
ContentHeaders: HttpHeaders;
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
ResponseText: text;
PostString: Text;
i: Integer;
Base64: text;
Convert: Codeunit "Base64 Convert";

begin

Content.GetHeaders(ContentHeaders);
ContentHeaders.Clear();
ContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
Codeblob.CreateOutStream(httploadOutStream, TextEncoding::UTF8);

poststring := '&action=get' +
'&apikey=' + apikey +
'&user=' + ftpuser +
'&pass=' + ftppassword +
'&ftpserver=' + ftpserver +
'&ftpdir=' + ftpdir +
'&ftpfilename=' + ftpfilename;

httploadOutStream.WriteText(poststring);

Codeblob.CreateInStream(httploadInStream, TextEncoding::UTF8);
Content.WriteFrom(httploadInStream);
Request.Content := Content;
Request.SetRequestUri('https://lessto.dk/cloudtofile/lessto-ftp-connect');
Request.Method := 'POST';

Client.Send(Request, Response);

Response.Content().ReadAs(ResponseText);

ResponseText := '[' + ResponseText + ']';
if not JsonResArray.ReadFrom((ResponseText)) then begin
errorno := '99';
errortext := 'Connection error';
exit;
end;

For i := 0 to jsonresarray.count - 1 do begin
jsonResarray.get(i, jsonrestoken);
JsonResObject := jsonrestoken.asobject;
end;

if jsonresobject.get('errorno', JsonResToken) then
errorno := jsonrestoken.asvalue.astext;

if jsonresobject.get('errortext', JsonResToken) then
errortext := jsonrestoken.asvalue.astext;

if jsonresobject.get('status', JsonResToken) then
status := jsonrestoken.asvalue.astext;

if jsonresobject.get('base64', JsonResToken) then begin
base64 := jsonrestoken.asvalue.astext;
textfile := convert.FromBase64(base64);
end;

if jsonresobject.get('serverfile', JsonResToken) then
serverfile := jsonrestoken.asvalue.astext;

end;

procedure lesstoFTPdelete(apikey: Text[100]; ftpuser: Text[20]; ftppassword: Text[20]; ftpserver: Text[20]; ftpdir: Text[100]; ftpfilename: Text[50]; var errorno: text[2]; var errortext: text[100]; var status: text[100]; var serverfile: text[200]);
var
CodeBlob: Codeunit "Temp Blob";
JsonResArray: JsonArray;
JsonResToken: JsonToken;
JsonResObject: JsonObject;
httploadOutStream: OutStream;
httploadInStream: InStream;
Content: HttpContent;
ContentHeaders: HttpHeaders;
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
ResponseText: text;
PostString: Text;
i: integer;

begin

Content.GetHeaders(ContentHeaders);
ContentHeaders.Clear();
ContentHeaders.Add('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
Codeblob.CreateOutStream(httploadOutStream, TextEncoding::UTF8);

poststring := '&action=delete' +
'&apikey=' + apikey +
'&user=' + ftpuser +
'&pass=' + ftppassword +
'&ftpserver=' + ftpserver +
'&ftpdir=' + ftpdir +
'&ftpfilename=' + ftpfilename;

httploadOutStream.WriteText(poststring);

Codeblob.CreateInStream(httploadInStream, TextEncoding::UTF8);
Content.WriteFrom(httploadInStream);
Request.Content := Content;
Request.SetRequestUri('https://lessto.dk/cloudtofile/lessto-ftp-connect');
Request.Method := 'POST';

Client.Send(Request, Response);

Response.Content().ReadAs(ResponseText);

ResponseText := '[' + ResponseText + ']';
if not JsonResArray.ReadFrom((ResponseText)) then begin
errorno := '99';
errortext := 'Connection error';
exit;
end;

For i := 0 to jsonresarray.count - 1 do begin
jsonResarray.get(i, jsonrestoken);
JsonResObject := jsonrestoken.asobject;
end;

if jsonresobject.get('errorno', JsonResToken) then
errorno := jsonrestoken.asvalue.astext;

if jsonresobject.get('errortext', JsonResToken) then
errortext := jsonrestoken.asvalue.astext;

if jsonresobject.get('status', JsonResToken) then
status := jsonrestoken.asvalue.astext;

if jsonresobject.get('serverfile', JsonResToken) then
serverfile := jsonrestoken.asvalue.astext;

end;

}