0

I wanna let to some users download some files from my website and I don't want them to see the physical path of the downloading file.

I moved the files in a folder outside of my web folder and use Response.WriteFile(filePath) to download them. this help me to hide the path but this method doesn't support resume download which I really want it.

So what method you recommend I use?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Majid Shahabfar
  • 4,010
  • 2
  • 28
  • 36
  • How do you think they know you're path? Where, to the public, are you exposing your physical path? – Grant Thomas Feb 08 '13 at 08:45
  • @Grant, I don't expose the physical path but you know when you download a file using a download manager such as IDM your physical path will be exposed. but as I said I'm using 'Response.WriteFile' which hides the physical path of the file but it is not resume support. – Majid Shahabfar Feb 08 '13 at 09:23

4 Answers4

1

Create a temporary directory and copy the file into there. You could even rename the file as something else so that it is not 'guessable'.

I am assuming if they can download the file, the contents of it are applicable to that person therefore there is no issue that user knowing the direct link. Others will not be able to guess the random directory and/or filename.

var directoryName = String.Format("{0}\{1}\{2}", 
Server.MapPath("original Path"),System.Guid.NewGuid().Replace("-",""), fileName);

This pretty much the same process we use to export information from our system.

ChrisBint
  • 12,773
  • 6
  • 40
  • 62
  • when user download a file for the first time I set an expiration date for that user in DB. so I wanted this user wouldn't be able to download that file after the expiration of his download time. – Majid Shahabfar Feb 08 '13 at 09:40
0

It's simple, I have done this before. Just hide the "input file" control, so it is hidden on the UI, then create a button which click event triggers the click event of the "input file" control. Then, you can also use javascript (in my case, it is JQuery), to take the name of the browsed file into a textbox which represent the name of the file.

Therefore, the file path is hidden from the UI.

Zan
  • 114
  • 12
0

I would implement a file handler (.ashx file) that serves the files based on some sort of id or name in the querystring and send it like this:

asp.net ashx handler prompting download instead of displaying file

Community
  • 1
  • 1
Dennisch
  • 6,888
  • 1
  • 17
  • 32
0

Here is the best approach:

    public static bool DownloadFile(HttpContext httpContext, string filePath, long speed)
    {
        // Many changes: mostly declare variables near use
        // Extracted duplicate references to HttpContext.Response and .Request
        // also duplicate reference to .HttpMethod

        // Removed try/catch blocks which hid any problems
        var response = httpContext.Response;
        var request = httpContext.Request;
        var method = request.HttpMethod.ToUpper();
        if (method != "GET" &&
            method != "HEAD")
        {
            response.StatusCode = 501;
            return false;
        }

        if (!File.Exists(filePath))
        {
            response.StatusCode = 404;
            return false;
        }

        // Stream implements IDisposable so should be in a using block
        using (var myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            var fileLength = myFile.Length;
            if (fileLength > Int32.MaxValue)
            {
                response.StatusCode = 413;
                return false;
            }

            var lastUpdateTiemStr = File.GetLastWriteTimeUtc(filePath).ToString("r");
            var fileName = Path.GetFileName(filePath);
            var fileNameUrlEncoded = HttpUtility.UrlEncode(fileName, Encoding.UTF8);
            var eTag = fileNameUrlEncoded + lastUpdateTiemStr;

            var ifRange = request.Headers["If-Range"];
            if (ifRange != null && ifRange.Replace("\"", "") != eTag)
            {
                response.StatusCode = 412;
                return false;
            }

            long startBytes = 0;

            // Just guessing, but I bet you want startBytes calculated before
            // using to calculate content-length
            var rangeHeader = request.Headers["Range"];
            if (rangeHeader != null)
            {
                response.StatusCode = 206;
                var range = rangeHeader.Split(new[] { '=', '-' });
                startBytes = Convert.ToInt64(range[1]);
                if (startBytes < 0 || startBytes >= fileLength)
                {
                    // TODO: Find correct status code
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    response.StatusDescription =
                        string.Format("Invalid start of range: {0}", startBytes);
                    return false;
                }
            }

            response.Clear();
            response.Buffer = false;
            response.AddHeader("Content-MD5", GetMD5Hash(filePath));
            response.AddHeader("Accept-Ranges", "bytes");
            response.AppendHeader("ETag", string.Format("\"{0}\"", eTag));
            response.AppendHeader("Last-Modified", lastUpdateTiemStr);
            response.ContentType = "application/octet-stream";
            response.AddHeader("Content-Disposition", "attachment;filename=" +
                                                        fileNameUrlEncoded.Replace("+", "%20").Replace(",",";"));
            var remaining = fileLength - startBytes;
            response.AddHeader("Content-Length", remaining.ToString());
            response.AddHeader("Connection", "Keep-Alive");
            response.ContentEncoding = Encoding.UTF8;

            if (startBytes > 0)
            {
                response.AddHeader("Content-Range",
                                    string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
            }

            // BinaryReader implements IDisposable so should be in a using block
            using (var br = new BinaryReader(myFile))
            {
                br.BaseStream.Seek(startBytes, SeekOrigin.Begin);

                const int packSize = 1024 * 10; //read in block,every block 10K bytes
                var maxCount = (int)Math.Ceiling((remaining + 0.0) / packSize); //download in block
                for (var i = 0; i < maxCount && response.IsClientConnected; i++)
                {
                    response.BinaryWrite(br.ReadBytes(packSize));
                    response.Flush();

                    // HACK: Unexplained sleep
                    var sleep = (int)Math.Ceiling(1000.0 * packSize / speed); //the number of millisecond
                    if (sleep > 1) 
                        Thread.Sleep(sleep);
                }
            }
        }
        return true;
    }

    static string GetMD5Hash(string input)
    {
        // Create a new instance of the MD5CryptoServiceProvider object.
        MD5 md5Hasher = MD5.Create();

        // Convert the input string to a byte array and compute the hash.
        byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

        // Create a new Stringbuilder to collect the bytes
        // and create a string.
        StringBuilder sBuilder = new StringBuilder();

        // Loop through each byte of the hashed data 
        // and format each one as a hexadecimal string.
        for (int i = 0; i < data.Length; i++)
        {
            sBuilder.Append(data[i].ToString("x2"));
        }

        // Return the hexadecimal string.
        return sBuilder.ToString();
    }
Majid Shahabfar
  • 4,010
  • 2
  • 28
  • 36