NetTalk Central
NetTalk Web Server => Web Server - Ask For Help => Topic started by: jking on April 27, 2012, 12:58:22 PM
-
I have part 1 of this project working, where I have PDF documents uploaded and stored in a BLOB. Now I want to be able to view the contents of the BLOB. My plan is to place a button at the end of each record in a browse, that when clicked, allows the user to view the PDF in the BLOB in a new browser window. From here they may save the pdf file or print it.
I have looked at example apps 26 and 40, as well as searched NetTalk Central for BLOB and sendfile articles. Here is my plan:
1. The button on the browse list calls p_web._SendFile(p_filename). The parameter p_filename will be filled with the actual filename of the pdf which is stored in a field of my file called XOFTCRF. For example, it may contain ABC.pdf.
2. My file XOFTCRF, has a unique ID called CRFID. The key on this field will be used to get the record currently selected in the browse, where needed.
3. Example 40 contains code in the _sendfile method of the WebHandler procedure. This allows the contents of the BLOB to be sent directly to the browser without first writing the file to disk. I'll modify this code to use my file, XOFTCRF, and get the BLOB contents of the currently selected record.
4. Assuming this is all correct, the browser should display the PDF contents (ABC.pdf) of the selected record, with the capability to save or print the PDF.
5. I should not need to create a NetWebPage with a Page Type set as File/PDF.
I have not tried this yet as I wanted to be sure I understood the basics first. Does this seem like a viable plan?
Thanks,
Jeff
-
Ok, I thought I would give this a try and added the following code:
In the _sendfile embed of the WebHandler I have the following:
p_web.ReplyContentType = 'application/pdf'
Access:XOFTCRF.Open()
Access:XOFTCRF.UseFile()
CRF:CRF_File_Name = 'web\' & sub(p_FileName,len(clip(self.site.WebFolderPath)) + 2,255)
if CRF:CRF_File_Name[1] = '/' then CRF:CRF_File_Name = sub(CRF:CRF_File_Name,2,255).
loop x = 1 to len(clip(CRF:CRF_File_Name))
if CRF:CRF_File_Name- = '/' then CRF:CRF_File_Name = '\'.
end
CRF:CRF_ID = p_web.GSV('CRF:CRF_ID')
If Access:XOFTCRF.Fetch(CRF:CRFID_key) = 0
loc:found = CRF:CRF_File{prop:size}
sendstring &= new(string(loc:found))
sendstring = CRF:CRF_File[1: loc:found]
! allows the browser to cache this file.
self.ForceNoCache = 0
self.HeaderDetails.CacheControl = ''
self.FileDate = today() - 200
self.FileTime = 6000 !clock()
! end of cache settings
self._SendMemory(sendString,loc:found)
dispose(sendString)
End
Access:XOFTCRF.Close()
If loc:found then return.
Then, in the button I added to the browse as type other, I added the following in the "user pressed button" embed:
p_web._SendFile(p_web.GSV('CRF:CRF_File_Name'))
Everything compiles fine. When I open the browse and press the button, nothing appears to happen. However, something is happening as I can no longer click on cancel or save, it appears to hang. I have used topspeed scan to look at the contents of the XOFTCRF file and there appears to be PDF content in the BLOB.
I would be glad to send a copy of this app but I would need to remove some templates that I use for BLOBs, and turn off SSL. I'm on NT 6.27.
Any ideas where I went wrong?
Thanks,
Jeff
-
I think an example would be best.
Perhaps you don't have to tweak your app as much as just make a simple example showing the effect you are getting - you can prime it with a data file that contains some PDF's already.
cheers
Bruce
-
Bruce,
I'll put together a test app and send it along shortly. In the meantime, I noticed another method called _sendBlob which takes just a filename as its parameter. Is this a working method and would it be better to use this instead of _sendFile?
Thanks,
Jeff
-
Bruce,
Attached is an example app, compiled as an exe. I normally compile as a dll and use the Multi-Host app with an SSL certificate. What I'm trying to accomplish is to allow the users to click on a button at the end of each record in the XOFTCRF browse and view the PDF stored in the BLOB. I don't want to have to create the pdf file first if possible, thus I'm using modified code from the _SendFile embed of example 40.
To get to the CRF browse, open the Study Patient browse from the main menu. Click on change and then go to the CRF tab. Here you will find the browse listing the PDF's. I want to click on the button at the end of each record and send the PDF to the browser for viewing or printing.
I have commented out some code since we are not creating any files. Hopefully you can show me where the problem is.
Thanks,
Jeff
[attachment deleted by admin]
-
Bruce,
I have made a bit of progress with this. However, it is a bit strange. I find I can only get the PDF contents of the BLOB to download if I do the following:
Open the web app, go to Browse, Study Patients, click on change to open the dummy patient. Next go to the CRF tab and click on change to open the record. Here is where it gets strange. If you now click on Cancel or Save, then the SendFile code fires and asks me to open or save the blob contents. It contiually does this and I must force the form to close.
So I have verified the blob contents are valid PDF documents, and the code to get the blob works. Now I just need to get the sendfile to fire only when I click the button at the end of each record in the browse.
Here is the code I currently have in the sendfile embed:
Access:XOFTCRF.Open()
Access:XOFTCRF.UseFile()
CRF:CRF_ID = p_web.GSV('CRF:CRF_ID')
p_web.HeaderDetails.ContentDisposition = 'attachment; filename="'&p_web.GSV('CRF:CRF_File_Name')&'"'
If Access:XOFTCRF.Fetch(CRF:CRFID_key) = 0
loc:found = CRF:CRF_File{prop:size} !blob size
sendstring &= new(string(loc:found))
sendstring = CRF:CRF_File[0: loc:found]
! allows the browser to cache this file.
!self.ForceNoCache = 0
!self.HeaderDetails.CacheControl = ''
!self.FileDate = today() - 200
!self.FileTime = 6000 !clock()
! end of cache settings
self._SendMemory(sendString,loc:found)
dispose(sendString)
End
Access:XOFTCRF.Close()
If loc:found then return.
Thanks,
Jeff
-
Hi Jeff,
I would be inclined to move your logic out of the _sendfile method and add it to servedocument net web file. You can copy the source file from the download files example. So on your button add the servedocument URL with a couple of parameters so you know what table, recordid blob etc you wish to serve.
I use this method to serve up created reports, Excel docs, HTML and docs from disk etc and all my logic sits in this one file.
Just a thought!
Cheers,
Kevin
-
Kevin,
Would I still be able to serve up documents from the blob? I don't really want to write files to disk first. Also, could you provide a short example of how to pass the table and blob I'd, etc.?
Thanks,
Jeff
-
In that case and after a quick look at "the book" yes you probably need to move the blob code back. However, I would still call the servedocument procedure.
To call a URL with parameters it is something like 'servedocument?TableName=' & p_web.gsv('TableName') & '&RecordID=' & p_web.gsv('RecordID')
I think the problem with the code you posted in the _sendfile embed (unless you didn't post everything) is that every time _sendfile is called (and it is called every time you do something) the code will fire if p_web.GSV('CRF:CRF_ID') has a value. So you would need to make sure you set p_web.GSV('CRF:CRF_ID') back to 0 after this code runs.
-
Hi Jeff,
before I look at your example, perhaps you want to follow Kevin's suggestion a bit. See example "File Download" for an example of a "serveDocument" procedure that takes parameters.
Cheers
Bruce
-
Bruce,
I took Kevin's advice about setting 'CRF:CRF_ID' back to zero. Actually, since this ID is used to filter the child browse, I did not think it a good idea to change this. So I created a new session variable called 'myCSFID'. I set this to zero at the beginning of the browse procedure, then again at the end of the sendfile code. In the 'user pressed button' embed, I set myCRFID = p_web.GSV('CFR:CFR_ID'), the ID of the currently selected record. I modified the sendfile code to use this new session variable as follows:
If p_web.GSV('myCRFID') <> 0 !
Access:XOFTCRF.Open()
Access:XOFTCRF.UseFile()
CRF:CRF_ID = p_web.GSV('myCRFID')
p_web.HeaderDetails.ContentDisposition = 'attachment; filename="'&p_web.GSV('CRF:CRF_File_Name')&'"'
If Access:XOFTCRF.Fetch(CRF:CRFID_key) = 0
loc:found = CRF:CRF_File{prop:size} !blob size
sendstring &= new(string(loc:found))
sendstring = CRF:CRF_File[0: loc:found]
self._SendMemory(sendString,loc:found)
dispose(sendString)
End
Access:XOFTCRF.Close()
If loc:found then return.
p_web.SSV('myCRFID', 0)
End!If
p_web.SSV('myCRFID', 0)
Now, the blob code in the sendfile embed does not appear to fire unless there is a value in the myCRFID session variable. I believe this is the result expected as Kevin indicated in his post. However, I still don't get the blob code to send the blob contents to the browser. Can you take a look at the example and see if you can help?
Thanks,
Jeff
-
Hi Jeff,
The logic from my reading of the book is that your button has a URL with a filename to serve. Within the _sendfile method you first check the blob for the file and if so extract that file and serve it up. If you have a URL on a button then any ss code on that button does not fire. So you need to have something in the URL so you know when to run your blob code. I think I would just try to get it working within the servedocument procedure like example 40 by first extracting the file to disk. Once that works then I think you can move some logic to the _sendfile method.
HTH's
Kev
-
Bruce and Kevin,
I think I have the solution to this. I removed the BLOB code from the _sendfile embed and placed it in the processed code embed of the ServeDocument procedure. This procedure is set page type OTHER. I then pass the CRF_ID and CRF_File_Name as parameters to this procedure. Here is the final code:
Access:XOFTCRF.Open()
Access:XOFTCRF.UseFile()
CRF:CRF_ID = p_web.GetValue('myCRFID')
p_web.ReplyContentType = 'application/pdf'
p_web.HeaderDetails.ContentDisposition = 'attachment; filename="'&p_web.GetValue('myCRFFile')&'"'
If Access:XOFTCRF.Fetch(CRF:CRFID_key) = 0
loc:found = CRF:CRF_File{prop:size} !blob size
sendstring &= new(string(loc:found))
sendstring = CRF:CRF_File[0: loc:found]
p_web._SendMemory(sendString,loc:found)
dispose(sendString)
End
Access:XOFTCRF.Close()
If loc:found then return.
I still have some clean-up to do but it is working. Let me know if you think there are any potential problems with this method. Thanks to both of you for helping with this.
Jeff