Historically, we've integrated PC data with our RPG programs by using Windows Networking and a drive letter mapped to an IFS directory. A user would be instructed to put an appropriate PC document in a particular IFS directory with a particular name, and an RPG program would use the IFS APIs to read that file name and process it. This method seemed like a good idea at the time, but I've found it to be very error prone. For whatever reason, my users don't seem to understand how to select the correct directories on the correct server, and they often mistype the filename. They're frustrated with the process, wondering why IT can't just do it for them. I'm frustrated, because this was supposed to be easy!
I found a much simpler and more reliable method: Uploading PC files with a web application. A web page asks the user to specify a file on her PC, and she selects it and clicks Upload, and the file is sent directly to my program for processing. This method works much better. The disadvantage was the complexity of receiving that file on the server. Until now.
On May 21, 2009, Giovanni Perotti of Easy400.net announced a new tool that makes file uploads fast and easy.
Traditional HTML forms encode data to be sent to the browser by using something known as "URL-Encoding." URL-encoding forces all the data entered by the user to use characters that are legal in an HTTP URL. To use that method with a file upload would be cumbersome and CPU intensive because there would be a lot of data that would need to be encoded. And what would be the benefit? Unlike traditional form data, there's little chance that a file upload would ever be used as part of a URL. Instead, it is used as POST data, a stream of data uploaded from the browser to the server. There's no need to URL-encode POST data, since the data isn't part of a URL. (However, the browser will still URL encode it by default!)
File uploads use an alternative method of encoding the data, and that method is called multipart/form-data. Multipart/form-data is very similar to the MIME formats used in email. It's a method of dividing one file upload (one big file of POST data) into multiple pieces and providing meta information about each piece. I won't give all the gory details here, though. Suffice it to say that a file upload can work only when you designate that an HTML form uses the POST method, and uses the multipart/form-data encoding.
Until recently, I was using PHP for all my file uploads because CGIDEV2 didn't support multipart/form-data. With Easy400.net's new tool, that problem is solved.
File upload support was contributed to Easy400.net by Ron Egyed who works for RJE Consulting in New Port Richey, Florida. This new support kicks in when you call the standard zhbGetInput() routine in CGIDEV2. For anyone who hasn't used it before, let me explain that ZhbGetInput() is a standard routine used in virtually all CGIDEV2 applications, and its job is to get all the input data from a browser, interpret it, decode any special encoding, and load the input fields into arrays inside CGIDEV2. A companion routine named zhbGetVar() is then called by your application for every input field you need to retrieve. Egyed's updates don't change that--you still call the CGIDEV2 routines in the same way.
However, when a file upload is provided in an HTML form, zhbGetInput() doesn't try to store the file's data into CGIDEV2's arrays. Instead, Egyed's code kicks in and puts the data into a temporary stream file (*STMF) in the IFS. CGIDEV2's arrays will just contain the filenames of the uploaded file. There are always file names, the name of the file on the PC it was uploaded from, and the name of the temporary file that Egyed's code has created in the IFS.
Let's look at an example. My Human Resources department has a digital camera, and they take photos of all new employees as part of the employee registration process. They'd like to upload those photos to be stored in the employee records on the i, and they'd like to be able to display those photos later by keying in the employee's ID number.
To accomplish that, I've given them the following HTML screen:
<form enctype="multipart/form-data" method="post"
action="/cgi-bin/mypgm.pgm">
Employee number:
<input type="text" name="empno" id="empno"
size="5" maxlength="5" value="/%empno%/">
Photograph (JPEG):
<input type="file" name="photo" id="photo"
maxlength="255" value="/%photo%/">
<input type="hidden" name="action" value="upload">
<input type="submit" value=" Ok ">
</form>
The enctype must be set to multipart/form-data. This is what provides the ability to upload files, as well as the ability to use the new feature of CGIDEV2. Furthermore, the method must be set to post.
The <input> HTML tag enables input from the user to be sent to the server. When the type of input is set to file, the user will have the option to select a file from his PC and upload it with the form. In this case, the name of the field that will contain the file being uploaded is photo. That means that CGIDEV2 will create a photo HTML variable containing the path to the file on the user's PC, as well as a photo_tempfile HTML variable that will contain the IFS pathname to the file. The actual data from the PC file will be put into the IFS under the name specified in the photo_tempfile variable.
Note: File type variables are always named using this same scheme. If you specify <input name=XXX>, the PC filename will be in an HTML variable named XXX, and the IFS filename will be in an HTML variable named XXX_tempfile.
The PC and IFS filenames can be read in your CGIDEV2 program by using the zhbGetVar() routine.
Now that you have the file in the IFS, you can (of course) simply move it to another spot in the IFS where you want to keep it, using the MOV or CPY CL commands. Or you can use the IFS APIs to read the file into your RPG program and process it. However, for the sake of example, let's say that the HR department wants employee information stored in a BLOB column in a database file that looks like this:
Create Table EMPPHOT (
empno decimal(5, 0) ,
photo blob(4M) allocate(0),
primary key (empno)
);
This file has only two fields: the employee number, which is used as a key to look up the picture, and a BLOB field containing the picture itself. Therefore, when I run my CGI program to process the file upload, it will want to retrieve the file from the IFS and insert it into the BLOB field in the database. Here's how I do that:
action = zhbGetVar('action');
select;
.
.
when action = 'upload';
InsertImage( zhbgetvar('empno')
: zhbgetvar('photo_tempfile')
: zhbgetvar('photo') );
wrtsection('result');
endsl;
.
.
P InsertImage B
D InsertImage PI
D cgi_empno 6a varying const
D cgi_ifsfile 255a varying const
D cgi_filename 255a varying const
D photo s sqltype(blob_file)
D empno s 5p 0
D exists s 10i 0
/free
monitor;
empno = %dec(cgi_empno: 5: 0);
on-error;
return;
endmon;
photo_name = cgi_ifsfile;
photo_dl = 0;
photo_nl = %len(%trimr(photo_name));
photo_fo = SQFRD;
exec SQL set option commit=*none,naming=*sys;
exec sql Select count(*) Into :exists From EmpPhot
where empno=:empno;
if sqlstt='00000';
if exists > 0;
exec sql update EmpPhot
set Photo=:Photo
where empno=:empno;
else;
exec sql insert into EmpPhot
values (:empno, :photo);
endif;
endif;
unlink(%trimr(photo_name));
/end-free
P E
It uses a SELECT SQL statement to see whether there's already a record for this employee. If there is, it uses an UPDATE SQL statement with a BLOB_FILE to change the image of the employee to the newly uploaded one. If the record doesn't exist, it uses the SQL INSERT statement to add it. In either case, SQL is told to read the contents of an IFS file (identified by the photo_tempfile HTML variable) and put the contents into the BLOB field in the database.
The unlink() API is called to delete the temporary IFS file. At this point, the data is already in the database, so the IFS file is no longer required.
So this article shows how to upload a file into a BLOB using the new routines from Easy400.net. A second article in this newsletter, Return a BLOB to a Browser [2], illustrates how to retrieve that BLOB and display it from an RPG program that uses the CGIDEV2 toolkit.
Download the sample code shown in this article. [3]
To learn more about the new support from Easy400.net, to see a sample program, or to download your own copy, visit Easy400.net [4] and click Deliverables and/or Downloads.
To learn more of the basics of web programming in RPG, read the following articles:
Links:
[1] http://systeminetwork.com/author/scott-klement
[2] http://systeminetwork.com/article/return-blob-browser
[3] http://www.pentontech.com/IBMContent/Documents/article/58107_925_UploadPic.zip
[4] http://www.easy400.net
[5] http://systeminetwork.com/article/web-programming-rpg-part-1
[6] http://systeminetwork.com/article/web-programming-rpg-part-2
[7] http://systeminetwork.com/article/web-programming-rpg-part-3