平台中用到的所有附件都是统一管理的,当然下载模块也是统一管理的
原先写的下载程序不支持多线程下载,并且无法支持续传,经网上搜索相关资料,得到一个比较好的函数,如下:
'输出文件到浏览器
'_fileName:输出的文件名
'_fullPath:实际文件(含路径)
'SaveFile:标志 True '
'_speed:下载速度 xxx 字节/秒
Public Shared Function ResponseFile(ByVal _fullPath As String, ByVal _fileName As String, ByVal SaveFile As Boolean, ByVal _speed As Long) As Boolean
If Trim(_fullPath) = "" Then Return False
If Trim(_fileName) = "" Then _fileName = Path.GetFileName(_fullPath)
_fileName = _fileName.Replace(" ", "") '删除空格
_fileName = _fileName.Replace("[", "") '
_fileName = _fileName.Replace("]", "") '
_fileName = _fileName.Replace("""", "") '
_fileName = _fileName.Replace("'", "") '
_fileName = _fileName.Replace("$", "") '
_fileName = _fileName.Replace("%", "") '
If _speed <= 0 Then _speed = 10000000000
Try
Dim input As New FileStream(_fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Dim reader As New BinaryReader(input)
Try
Current.Response.ClearContent()
Current.Response.ClearHeaders()
Current.Response.AddHeader("Accept-Ranges", "bytes")
Current.Response.Buffer = False
Dim length As Long = input.Length
Dim offset As Long = 0
Dim num3 As Double = 10240
Dim millisecondsTimeout As Integer = (CInt(Math.Floor(CDbl(((1000 * num3) / CDbl(_speed))))) + 1)
If (Not Current.Request.Headers.Item("Range") Is Nothing) Then
Current.Response.StatusCode = 206
offset = Convert.ToInt64(Current.Request.Headers.Item("Range").Split(New Char() {"="c, "-"c})(1))
End If
Current.Response.AddHeader("Content-Length", (length - offset).ToString)
Current.Response.AddHeader("Connection", "Keep-Alive")
Current.Response.ContentType = GetFileContentType(Path.GetExtension(_fileName))
Current.Response.AddHeader("Content-Disposition", (IIf(SaveFile, "attachment;", "") & "filename=" & HttpUtility.UrlEncode(_fileName, Encoding.UTF8)))
Current.Response.ContentEncoding = Encoding.Default
reader.BaseStream.Seek(offset, SeekOrigin.Begin)
Dim num5 As Integer = (CInt(Math.Floor(CDbl((CDbl((length - offset)) / num3)))) + 1)
Dim i As Integer
For i = 0 To num5 - 1
If Current.Response.IsClientConnected Then ' 检查客户端是否在线
Current.Response.BinaryWrite(reader.ReadBytes(Integer.Parse(num3.ToString))) ' 读取数据到缓冲区
System.Threading.Thread.Sleep(millisecondsTimeout)
Else
i = num5
End If
Next i
Catch
Return False
Finally
reader.Close()
input.Close()
End Try
Current.Response.End()
Catch
Return False
End Try
Return True
End Function
需要Imports System.Web.HttpContext
上述函数就能够支持多线程下载了,并且支持限速。
接下来就是考虑如何防范系统外部的用户盗链和系统内部其他用户的盗链(越权)访问
这方面我一直不知道有什么比较好的解决方案,下载模块调用入口原本是INC/ShowAttachFile.Aspx?File_ID=xxxx
其中的File_ID是真实的索引ID,通过这个索引ID就能够下载数据
防止外部盗链的方法很容易,只要判断帐号有没有登录就解决了,难就难在如何防止当前用户把这个地址发给其他用户,其他用户得到这个地址后能否获得文件,如果能够获得文件,那就是越权访问了(他本没有权限访问该文件的)
有不少的方法,有的人说,在后台增加一个授权字段,把授权情况写到该字段里面,在页面里进行验证,这种方法当然具有可行性,问题是过于复杂,整个系统到处都有可能用到了附件,我们的系统授权都是针对菜单进行授权的,并不是针对附件的,如果要有,无疑增加了授权的复杂性。
所以我在想一种更简单的解决方案
我个人认为关键还是出在下载模块入口上INC/ShowAttachFile.Aspx?File_ID=xxxx
如果我们的xxxx不传送真实的索引ID,而是传送一个真实的索引ID和当前用户作密码一块加密后的数据(密文),然后在下载模块中将该密文还原成真实索引ID,不就解决了越权访问问题吗?
我的系统就是这样解决越权问题的
用到一队加解密算法
密文=加密(真实的文件ID,密码)
其中密码采用当前系统的登录帐号
下载模块就改成了INC/ShowAttachFile.Aspx?File_ID=密文这种调用
在下载模块第一步操作就调用了解密算法
真实的文件ID=解密(密文,密码)
加密,解密算法是一对可逆算法,两者只要密码相同,就能够还原数据,这种算法网上非常多。
因为这个算法用到了密码跟当前登录名有关,所以如果当前登录名不同,这个链接地址拷贝给了别人之后,他是无法解密得到正确的文件ID号的,也就无法越权访问文件了。