package manager

import (
    "net/http"
    "regexp"
    "strconv"
    "io"
    "strings"
    "fmt"
    "time"
)

var publicUrlRegex *regexp.Regexp

func init() {
    var err error
    publicUrlRegex, err = regexp.Compile("/([0-9]*)/([0-9]*)/(.*)")
    if err != nil {
        panic(err)
    }
}
//公共资源处理器
func (vm *VolumeManager)publicEntry(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet, http.MethodHead:
        if publicUrlRegex.MatchString(r.URL.Path) {
            vm.publicReadFile(w, r)
        } else {
            http.NotFound(w, r)
        }
    default:
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}
//读取公共资源文件
func (vm *VolumeManager)publicReadFile(w http.ResponseWriter, r *http.Request) {
    match := publicUrlRegex.FindStringSubmatch(r.URL.Path)

    vid, _ := strconv.ParseUint(match[1], 10, 64)
    volume := vm.Volumes[vid]
    if volume == nil {
        http.Error(w, "can't find volume", http.StatusNotFound)
        return
    }
    fid, _ := strconv.ParseUint(match[2], 10, 64)
    file, err := volume.Get(fid)
    if err != nil || file.Info.FileName != match[3] {
        http.NotFound(w, r)
        return
    }
    w.Header().Set("Content-Type", get_content_type(file.Info.FileName))
    w.Header().Set("Accept-Ranges", "bytes")
    w.Header().Set("ETag", fmt.Sprintf("\"%d\"", fid))
    //暂时不使用Last-Modified,用ETag即可
    //w.Header().Set("Last-Modified", file.Info.Mtime.Format(http.TimeFormat))
    w.Header().Set("Expires", time.Now().In(time.UTC).Add(DefaultExpires).Format(http.TimeFormat))
    etagMatch := false
    if r.Header.Get("If-None-Match") != "" {
        s := r.Header.Get("If-None-Match")
        if etag, err := strconv.ParseUint(s[1:len(s) - 1], 10, 64); err == nil && etag == fid {
            etagMatch = true
        }
    }
    if r.Header.Get("Range") != "" {
        ranges := strings.Split(r.Header.Get("Range")[6:], "-")
        start, err := strconv.ParseUint(ranges[0], 10, 64)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        length := uint64(0)
        if start > file.Info.Size {
            start = file.Info.Size
        } else if ranges[1] != "" {
            end, err := strconv.ParseUint(ranges[1], 10, 64)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            end += 1
            if end > file.Info.Size {
                end = file.Info.Size
            }
            length = end - start
        } else {
            length = file.Info.Size - start
        }
        w.Header().Set("Content-Length", strconv.FormatUint(length, 10))
        if length == 0 {
            w.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
            return
        }
        w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, start + length - 1, file.Info.Size))

        if etagMatch {
            w.WriteHeader(http.StatusNotModified)
            return
        }

        w.WriteHeader(http.StatusPartialContent)

        if r.Method != http.MethodHead {
            file.Seek(int64(start), 0)
            io.CopyN(w, file, int64(length))
        }
    } else {
        w.Header().Set("Content-Length", strconv.FormatUint(file.Info.Size, 10))
        if etagMatch {
            w.WriteHeader(http.StatusNotModified)
        } else if r.Method != http.MethodHead {
            io.Copy(w, file)
        }
    }
}

public_handers.go的更多相关文章

随机推荐

  1. LeetCode(34)-Palindrome Number

    题目: Determine whether an integer is a palindrome. Do this without extra space. 思路: 求一个整数是不是回文树.负数不是, ...

  2. 《老罗的Android之旅》导读PPT

    虽然好几个月没更新博客了,但是老罗一直有在准备可以分享的东西的.除了早前在微博分享Android4.2相关技术之外,这次还特意准备了13个PPT,总结之前所研究过的东西.内容从Android组件设计思 ...

  3. JS基础速成(三)- DOM(文件对象模型)

    .t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.DOM树的基本结构 DOM节点分为三大类:元素节点(标签节点),属性节 ...

  4. css 字体两端对齐

    我想作为一个前端工作者,总会遇到这样的场景,一个表单展示的字段标题有4个字也有2个字的时候,这样子同时存在想展示的美观一点,就需要字体两端对齐了,其实实现方式很简单,我针对其中一种来做下介绍,以后方法 ...

  5. MFC学习问题总结

    1.学习MFC添加位图,无法获取其ID 1).点击视图->其他窗口->资源视图,你会发现“无法在此窗口显示”,找到resource.h文件,关闭即可重新走一遍上面的过程就会发现可以打开了. ...

  6. 使用nginx缓存服务器上的静态文件

    一.nginx缓存的优点 如图所示,nginx缓存,可以在一定程度上,减少源服务器的处理请求压力. 因为静态文件(比如css,js, 图片)中,很多都是不经常更新的.nginx使用proxy_cach ...

  7. JavaScript路线

    看到知乎上有大神回答的,感觉很不错,分享下 首先要说明的是,咱现在不是高手,最多还是一个半桶水,算是入了JS的门. 谈不上经验,都是一些教训. 这个时候有人要说,“靠,你丫半桶水,凭啥教我们”.您先别 ...

  8. Python测试远程端口连接时间

    问题 最近自己服务器访问别人的服务器,有时候会报超时错误,有时候又能够正常访问别人服务器. 思路 最开始猜测是网络不稳定造成的,但是自己没有收集什么时候超时,什么时候能正常访问别人服务器的日志,搞网络 ...

  9. 老司机告诉你高质量的Java代码是怎么练成的?

    一提起程序员,首先想到的一定是"码农",对,我们是高产量的优质"码农",我们拥有超跃常人的逻辑思维以及不走寻常路的分析.判别能力,当然,我们也有良好的编码规范, ...

  10. 浅谈java中的"=="和eqals区别

    在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String(&qu ...