How to download a file using Webspeed

JeffCook

New Member
I need to be able to send a file to a user's computer from our webspeed website. I've seen plenty of examples of uploads (we have a webpage that does that already). But I've not seen any examples using webspeed of allowing the user to download a file to their computer.

Does anyone have any examples they could share or point me to?

Thanks.
 

jongpau

Member
Just put a link to the file on your page (can of course be done dynamically). The user can then click the link to download the file. In the Web world the user should always have control over what does and does not get downloaded to their computers and where it gets stored.
 

JeffCook

New Member
That didn't work. My setup is that the file is on a Linux box with webspeed servers running on it and the website is hosted on a windows server. How would I make it visible to the webserver to let the user download it?

My apologies for not giving that information in my initial post.
 

jongpau

Member
You'll have to somehow make the file available to the web server. Maybe you can look into using Samba on the Linux box and map it as a network drive on the Windows box. That way your web server will have access to the file.
 

Casper

ProgressTalk.com Moderator
Staff member
You could also use webspeed to do it. Put the right content-type header on the file and stream it back to the webserver.

small example to give you an idea of what I mean:
Code:
IF c_file <> ?
THEN DO:
   INPUT FROM VALUE(c_file) BINARY NO-MAP NO-CONVERT.
   /* determine length */
   SEEK INPUT TO END.
   i_len = SEEK(INPUT).
   /* position at begin */
   SEEK INPUT TO 0.
   /* Output header */
   {&OUT} "Content-Type: " + (IF cp_doctype = 'DOC' THEN "application/rtf" 
   ELSE IF cp_doctype = 'SCA' THEN "image/tiff" 
   ELSE IF cp_doctype = 'MAI' THEN "application/msoutlook"
   ELSE '') SKIP
   "Accept-Ranges: none" skip
   "Content-Length: " i_len format ">>>>>>9" skip (1).
   /*stream file to webpage in 8k blocks (pick a number :-) ) */
   ASSIGN LENGTH (ra_rawdata) = 8192.
   REPEAT:
      IMPORT UNFORMATTED ra_rawdata.
      PUT {&WEBSTREAM} CONTROL ra_rawdata.
   END.
   ASSIGN LENGTH (ra_rawdata) = 0. /* Deallocate memory */
   INPUT CLOSE.
   RETURN ''.
END.

HTH,

Casper.
 

JeffCook

New Member
Well, I've tried both suggestions. I copied the file manually to the webserver and did a link to it and I get the following error:

Compile Error: ./test.xls


** Unable to understand after -- "ÐÏࡱ". (247)
** ./test.xls Could not understand line 1. (198)

Unable to run Web object 'test.xls'

When I use the method that Casper suggested it throws the binary of the file to the screen as part of the webpage.

I'm getting very confused. :confused:
 

FrancoisL

Member
Well, I've tried both suggestions. I copied the file manually to the webserver and did a link to it and I get the following error:

Compile Error: ./test.xls


** Unable to understand after -- "ÐÏࡱ". (247)
** ./test.xls Could not understand line 1. (198)

Unable to run Web object 'test.xls'

Your not linking to your file properly. You cannot put it in the Webspeed working directory and link to it like it was a webspeed program. Your server is trying to run your file like it was a webspeed page.

You have to put your file in the www root directory or a directory under that. Then you link to it without using a dot example : /excel/test.xls
this will look at the root of your webserver for a directory called excel with a file test.xls.

So if your www root directory is /var/www then you put the file in /var/www/excel/test.xls
 

JeffCook

New Member
Thanks, FrancoisL,

That worked. How do I get this file moved over to the web root as it is dynamicaly created/re-created when they run the report? COPY-LOB just moves it to my working directory and if I put in the root directory the page errors out because it can't find the web root directory.

Is this where I would need to do a Samba mapping to the web server so the file is available to download as Casper suggested?
 

sdjensen

Member
First you will need to change the content-type if the file is an excel file.

After the line
Code:
ELSE IF cp_doctype = 'MAI' THEN "application/msoutlook"
add
Code:
ELSE IF cp_doctype = 'xls' THEN 
"application/vnd.ms-excel"

Where is you file at the beginning, in the database or as a file?
if the file is in the database you can use copy-lob from database to a memptr and output that to user as the file.
 

JeffCook

New Member
The file is a file on the linux server. Trying to make it accessible to the web server is proving to be tricky. Would using copy-lob from the file to a memprt and then out to a file to the user work as opposed to it being straight from the database? I'll try that and see if it makes it downloadable.

It's been fun trying to dredge all this back up after a year and a half. I appreciate everyone's patience with me.
 

Cecil

19+ years progress programming and still learning.
Please can you give an example of your link (URL) which is in your HTML Page.

The file does not need to exist on the Web Server as the file is delivered to the Client's Web Browser via the CGI Messenger.

Yes you can use copy-lob to copy the file to a memptr then use:
Code:
COPY-LOB FROM FILE chFilename TO OBJECT mpData.
{&OUT-LONG} mpData SKIP.
SET-SIZE(mpData) = 0.
Here is an example of a URL which I have in my HTML.

Code:
<a href="http://www.domain.co.nz/IA/filedownload.html?filename=latetestreport.pdf">File Download</a>
The IA is a script aliasfor the CGI Messenger

filedownload.html is just webspeed code which will then deliver the requested file (latetestreport.pdf).

Hope this helps. Or possibly raises more questions.
 

JeffCook

New Member
Using Cecil's suggestion, I have an html page called getorders_dl.html that does the export of the file as a memptr data type.

Here's my URL link for trying to download the file:
Code:
<a href="getorders_dl.html?filename=/apps/trade/out/test.xls">/apps/trade/out/test.xls</a>

When I click on the link it gives me the message "Unable to run Web object 'getorders_dl.html'".

I have a button that also calls the getorders_dl.html and when it runs it spews the contents of the .xls file to the screen in binary format. This happened when I tried Casper's suggestion no matter what I set the Content-Type to be.

At this point I may just go with the drive mapping to get the file out to the user because nothing else seems to work.
 

Cecil

19+ years progress programming and still learning.
If you are testing using FireFox you can install a Add-on Tool called "Live HTTP Headers"

The this will show you what the HTTP Header content is being returned back to the Browser.

It's sound to me that your are about 95% of the way there. Just need to debug the HTTP header content using the "Live HTTP Headers" tool will help you out.

Here is a snippet of code from my WebSpeed application. This will try (not always) and force the browser to download and save:

Code:
 IF GET-SIZE(mpPDFDOCO) GT 0 THEN
  DO:

    output-http-header("Content-Description", "File Transfer").
    output-http-header("Content-Type", "application/force-download").
    output-http-header("Content-Type", "application/octet-stream").
    output-http-header("Content-Type", "application/download").
    output-http-header("Content-Disposition", "attachment; filename=MembershipFile.pdf").
    output-http-header("Content-Transfer-Encoding", "binary").
    output-http-header("Expires","0").
    output-http-header("Cache-Control", "no-cache, must-revalidate").
    output-http-header("Content-Length",STRING(GET-SIZE(mpPDFDOCO))).
    output-content-type("":U).
    /* Spit the data out to the web stream */
    {&OUT-lONG} mpPDFDOCO.
Hope it helps good luck.
 

Casper

ProgressTalk.com Moderator
Staff member
we use forms all the time. I made a simple javascript which does a submit when the link is clicked, sends the right parameters (for my dispatch procedure) and then uses the source I gave earlier to return the document.
Wether it is opened in the webbrowser or by the associated application outside the bowser has to do with the characteristics of that application and is most times user configurable. (Like for instance pdf kan open in browse but also in acrobat reader outside the browse: pdf--> edit --> preferences--> Internet --> Display pdf in browser, outlook always opend outside of the browse )
I mean to say that's outside the reach of you.

Casper.
 

webguy

Member
Hi,

I use fckeditor with my webspeed form to allow a user to upload an image through an inline editor. Similiar to the inline editor used on this forum.

So basically I have my html form and I just use use the fckeditor for the area where you input text.

Fckeditor jquery plugin and examples
http://www.fyneworks.com/jquery/FCKEditor/

jquery
http://jquery.com/

And you will just need to download the fckeditor. For the upload feature you can use the PHP piece.
http://www.fckeditor.net/


Code:
<script language="speedscript">
def var v-form-name     as char  no-undo.
def var v-form-id       as char  no-undo.
def var v-textfld-id    as char  no-undo.
def var v-textfld-name  as char  no-undo.
def var v-form-action   as char  no-undo.
def var v-editor-type   as char  no-undo.
 
/* form definitions */
assign v-form-name    = "form1"
       v-form-id      = "form1"
       v-textfld-id   = "desc"
       v-textfld-name = "desc"
       v-form-action  = "test.html"
       v-editor-type  = "default".  /* config defined in fckconfig.js */
</script>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[URL]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/URL]">
<html xmlns="[URL]http://www.w3.org/1999/xhtml[/URL]">
<head>
 <title>My form</title>
 
 <!-- ahh jquery library -->
 <script type="text/javascript" src="/js/jquery-1.2.6.min.js"></script>
 
 <!-- jquery fckeditor plugin -->
 <script type="text/javascript" src="/js/FCKEditor1.3.js"></script>
 
 <script language="javascript">
  //# jquery used to embed fckeditor by simply attaching
  //  it to the id of a textfield
  $(function(){
   $.fck.path = '/fckeditor/';
   $('#`v-textfld-id`').fck({toolbar: '`v-editor-type`', height: 300, width: 400 });
  });
 </script>
 
</head>
<body>
<div id="wrapper">
  <div class="form-container">
    <div class="forminfo">
      <p class="forminfotitle">Add Info</p>
    </div>
    <!-- FORM -->
    <form name="`v-form-name`" id="`v-form-id`" method="post" action="`v-form-action`">
    <input type="hidden" name="mode" value="add">
       <p class="legend"><strong>Note:</strong> Required fields are marked with an asterisk (<em>*</em>)</p>
       <fieldset>
         <legend>Add News Event</legend>
         <div>
           <label for="Description">Description <em>*</em></label>
           <textarea name="`v-textfld-name`" id="`v-textfld-id`"></textarea>
         </div>
         <div class="buttonrow">
           <input type="submit" value="Submit" class="button" />
         </div>
       </fieldset>
    </form>
  </div>
 </div>
</body>
</html>
 

JeffCook

New Member
Since I'm having issues with this and my deadline is looming near, I've decided to go with setting up a directory on my webspeed server box and mounting it as a remote directory in IIS (v6).

Now it still doesn't see the file. If I copy the file over to the webserver it sees the file and brings up the download dialog box. How do I get the webserver to see the files in the shared directory?

:confused:
 

FrancoisL

Member
Since I'm having issues with this and my deadline is looming near, I've decided to go with setting up a directory on my webspeed server box and mounting it as a remote directory in IIS (v6).

Now it still doesn't see the file. If I copy the file over to the webserver it sees the file and brings up the download dialog box. How do I get the webserver to see the files in the shared directory?

Your looking at the wrong spot . Casper suggestion about streaming the file as a binary stream was the perfect solution for your problem . You open the file , output the right header , and stream the binary data to the user .

The only part missing in his example is the right header type but that depending on the type of file you want to send.
 

JeffCook

New Member
Trying Casper's solution I have received a multitude of different errors depending on what I include or don't include in the http header info (using Cecil's example of some the different options available). I've gone from getting CGI Misbehaving to missing or bad http headers to "Internet Explorer cannot display the webpage".

Code:
{&OUT}  "Content-Type: application/vnd.ms-excel" skip
   "Content-disposition: attachment; filename=" + ip_filefullpath skip
   "Accept-Ranges: none" skip
   "Content-Length: " filelength format ">>>>>>>>9" skip(1).
 
   LENGTH(vfileline) = 8192. 
   repeat:
      import stream inputfile unformatted vfileline.
      put {&WEBSTREAM} CONTROL vfileline.
   end. /* repeat */
   /* Clean-up: Raw variable should be reset to deallocate the memory
       and the stream should be closed.  */                                                                 
   LENGTH(vfileline) = 0.

This code gives me the "Internet Explorer cannot display the webpage" error.
 

webguy

Member
Caspers solution will work. I mis-understood what you are trying to do.

If your files are spreadsheet from a query then forget creating and uploading. Just stream the data to the browser and prompt them to download the file. In that case you dont need to upload the file, you just do your query and then stream the output to the browser.

This is a basic example of streaming the output of a query to the browser which will prompt the user to download the file.


a test example. copy this form test.html

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[URL]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/URL]">
<html xmlns="[URL]http://www.w3.org/1999/xhtml[/URL]">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>test</title>
</head>
<body>
<form id="streamtest" name="streamtest" method="post" action="stream_out.html">
  <input type="submit" name="button" id="button" value="Submit" />
</form>
</body>
</html>

Then create another html file (webspeed html file) with this code and call it stream_out.html:

Code:
<script language="speedscript">
 def var v-random-num  as int  no-undo.
 def var v-file-name   as char no-undo.
 /* form is submitted using post in production change to eq post */
 if request_method eq "post" then
 do:
    /* report title */
    {&out} 'ORDERS: File Generated on ' string(today) ' at ' string(time, "HH:MM AM") skip(2).
 
    /* generate headers for report example headers */
    {&out} '"Account",'
           '"Order Date",'
           '"Invoice",'
           '"Order Qty"' skip.
    /* do your query. example query        */
    /* for each order ....                 */
    /* output formated for excel           */
    /*   {&out} '"' order.acct '",'        */
    /*          '"' order.order-date '",'  */
    /*          '"' order.invoice '",'     */
    /*          '"' order.qty '"'.         */
    /* end. */
 
    /* now stream to browser as a csv using output headers */
    procedure output-header:
      assign v-random-num = etime
             v-file-name  = "account" + trim(get-value("acctlist")) + "-" + trim(get-value("report_type")) +
                            string(v-random-num) + ".csv".
      if index(HTTP_USER_AGENT, "MSIE") ne 0 then
      do:
         output-http-header ("content-disposition", "attachment~; filename=" + v-file-name ).
         output-content-type ("text/plain").
      end.
      else
      do:
         output-http-header ("content-disposition", "inline~; filename=" + v-file-name ).
         output-content-type ("application/octet-stream").
      end.
    end procedure.
 end.
</script>

click submit on the form and it will call stream_out.html. it will output the results of the query (you would have to enter your own query) to a csv file and in the browser the user will get the download prompt. If you just want to test just copy without any query and at least you can see how it outputs the file with the title and headers. This is just a basic example.
 
Top