Link to home
Start Free TrialLog in
Avatar of kurukken
kurukken

asked on

Trying to hide the path to a download file using content-dispostition

Hi,

I'm trying to make a file available for download, but do not want to reveal its absolute path. The following code does work with small files (100K), but fails with anything large:


<%
Response.Expires = 0
if session("username") <> "" then
      Response.ContentType = "application/zip"
      Response.AddHeader "Content-Disposition", "attachment; filename=blah4.zip"
      Session("DL1") = ""
Else
      Response.redirect ("https://dl.company.com/nologin.htm")
End If

%><!-- #INCLUDE FILE = secret/secretfile.zip //-->

Questions:
1) Is there something I'm missing or is there a system limitation?

2) Is this even the right approach? How would i secure files that I don't want the users to have access to, unless they are authenticated?

Thanks.
Avatar of fritz_the_blank
fritz_the_blank
Flag of United States of America image

This might help you:


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
<%
Function downloadFile( strFile, strDownloadFilename )
     Dim strFilename,objStream,objFilesystem,objFilestream
     Dim intFileLength
     ' get full path of specified file
     strFilename = Server.MapPath( "../InProgress/Uploads/"  & strFile)
     ' clear the buffer
     Response.Buffer = True
     Response.Clear

     ' create stream
     Set objStream = Server.CreateObject("ADODB.Stream")
     objStream.Open

     ' set as binary
     objStream.Type = 1

     ' check the file exists
     Set objFilesystem = Server.CreateObject("Scripting.FileSystemObject")
     if not objFilesystem.FileExists(strFilename) then
          Response.Write("<h1>Error</h1>: " & strFilename & " does not exist<p>")
          Response.End
     end if


     ' get length of file
     Set objFilestream = objFilesystem.GetFile( strFilename )
     intFilelength = objFilestream.size
 
     objStream.LoadFromFile( strFilename )
     if err then
          Response.Write("<h1>Error: </h1>" & err.Description & "<p>")
          Response.End
     end if
     
     'format strFileName
     if Len( Trim(strDownloadFilename) ) > 0 then
          strDownloadFilename = Trim( strDownloadFilename )
     else
          strDownloadFilename = objFilestream.name
     end if

     ' send the headers to the users browser
     Response.AddHeader "Content-Disposition", "attachment; filename=" & strDownloadFilename
     Response.AddHeader "Content-Length", intFilelength
     Response.Charset = "UTF-8"
     Response.ContentType = "application/octet-stream"

     ' output the file to the browser
     Response.BinaryWrite objStream.Read
     Response.Flush

     ' tidy up
     objFilestream.Close
     Set objFilestream = Nothing
End Function
%>
</HEAD>
<BODY>
<%
Call downloadFile( Replace( Request("FILE") ,"/","\"), Request("FILENAME") )
'Response.Write (Request("FILE")  & "----" & Request("FILENAME") )
%>

</BODY>
</HTML>
Just modify this line:

 strFilename = Server.MapPath( "../InProgress/Uploads/"  & strFile)

to match your environment and make sure that the IUSR_ account has Read access to the directory that houses your files.

FtB
Avatar of msice
msice

To fix the tmeout for large files use <% Server.ScriptTimeout = 700 %>
Avatar of kurukken

ASKER

Hi,

I gave it a shot and I'm getting:

Error
: D:\WWW\Inetpub\wwwroot\download\temp\test does not exist

The above path does physically exist and iuser account has read and execute privilages.
The asp page resides in the same directory as the "test" directory.

Verified that:
strFilename = Server.MapPath( "test/"  & strFile)

do I have to do anything with the "strFile"? I tried declaring the filename above the code you provided but no dice. Same error.

Then NOW I realized that you gave me a function. Sorry for being a dope, how do I call this function?
Yes, you would call it like this:

downloadFile( strFile, strDownloadFilename )

where strFile is the name of the file as it exists on the server and strDownloadFilename is the name that you want your user to see, for example:

downloadFile( "jane's Resume 2.doc", "resume.doc")

FtB
Hi,

thanks.  Appologies - I'm new to asp. So here's what I did:

<BODY>
<%
Call downloadFile( Replace( Request("FILE") ,"/","\"), Request("FILENAME") )
'Response.Write (Request("FILE")  & "----" & Request("FILENAME") )
downloadFile("the-test-file.exe", "testing.exe")
%>

</BODY>
</HTML>

This time round, i got a:

HTTP 500 - Internal server error
Internet Explorer

So I checked the iis logs and saw this line associated with the asp page:
Cannot_use_parentheses_when_calling_a_sub

which tells me that I'm doing something wrong.

Okay, you're close (just being a little too literal or I was being a little too abstract). Please try this:

<HTML>
<BODY>
<%
call downloadFile("the-test-file.exe", "testing.exe")
%>

</BODY>
</HTML>
Thanks for your patience.

i think i"m narrowing the problem made that change and now I get this:

Execution of the ASP page caused the Response Buffer to exceed its configured limit.

So, this is now an IIS issue? I've started poking around. Any clues?
Okay, this is good. At least now the function appears to be called and it seems to be doing its thing.

Before we chase down the server configuration, would you please try testing with a very small file to make sure that we are hunting down the correct problem?

FtB
It works with a small file!

Come to think of it, i was getting the same problem with my code above (I like your code better though!)
To fix the tmeout for large files use <% Server.ScriptTimeout = 700 %>
I don't know that this is a timeout issue (although it may become one after this issue is set).

Try putting this as the first line on your page:

<%Response.Buffer = False%>
@msice--

I see that you have posted that idea twice, and it is a good one for long scripts. The reason why I am not echoing it is because none of the errors indicate a script time out. Now, once we get everything else fixed, the script timeout might become an issue, but I think that these other problems are the real culprit.

Fritz the Blank
It fixed my issue with this same type of error as the file begins to send the page was timing out causing the file not to download.
But would that throw a " Response Buffer to exceed its configured limit" error?

FtB
fritz:

While working with the small file, I tried the <%Response.Buffer = False%>, but it conflicted with the line within the function. So I deleted it and turned the one in the function to false and got an internal error.


msice:
i set the time out as you suggested, no luck. I don't think the script is timing out, I think there's some buffer limit that's being exceeded by large files.
Is there any other code on the page besides this?
I googled this and found a response:

An ASP application uses internal server buffers when writing data to
the client, irrespective of the value of Response.Buffer. As the
application is writing data synchronously, the data buffer can be
reused when the BinaryWrite function returns, while the server is
writing the data asynchronously to the client. The server, in turn,
makes a copy of the data into its internal buffers. As a result of
this buffering, the ASP application should not try to send
unreasonable amounts of data in a single BinaryWrite, but rather break
it into fragments so as to avoid running out of buffer space. Should
the buffer space be exceeded, ASP error 251, "response exceeds the
buffer limit," will be returned. While the default maximum buffer size
is 4MB, the server administrator may increase it.

The maximum buffer size can be increased by changing the AspBufferingLimit
property.  You can do this by downloading the IIS 6.0 Resource Kit Tools
and change the property using MB Explorer. Here is a link to the IIS 6.0
Resource Kit Tools.

http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en

For more information on the AspBufferingLimit property, see the below link.

http://www.microsoft.com/technet/treeview/default.asp?url=/technet/prodtechnol/windowsserver2003/proddocs/standard/ref_mb_aspbufferinglimit.asp


What do you think??
Just saw your response. I copied your code into a blank page and have been using that for testing. Not modified it except for the path.
Give it a try....

BTW, did you see my last question? How large is this file?

FtB
The file is 21267.94KB (21778367 bytes)

I'd rather not muck with the metabase if I can help it. I'm concerned about concurrant downloads of this file. Will it kill the memory on my server if it uses up 21MB for each download?

If you've gotten it to work with a file larger than 4mb without this fix, then I'm going to try your solution first.

ASKER CERTIFIED SOLUTION
Avatar of fritz_the_blank
fritz_the_blank
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I thought about that, and mentioned it above, but as kurukken observes, the code requires buffering to be enabled.

FtB
Tried both. and Yup, it appears that there is a 4 meg limit. I'm now going to look for objects out there that allow me to download files outside the iis webroot.
there is something about chunking that can deal with this, but I don't remember the details...

FtB
You can also do this.

if session("username") <> "" then
    Response.Write("<script>window.open("./path/filename",null,"toolbar=no,menubar=no,location=no")</script>")
Else
     Response.redirect ("https://dl.company.com/nologin.htm")
End If

Just some alternative suggestions. :)