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