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. Python的lambda

    if else 可以用简单的三元运算符表示 if 1 == 1: name = 'wupeiqi' else: name = 'alex' --> name = 'wupeiqi' if 1 = ...

  2. Memcache架构新思考

    2011年初Marc Kwiatkowski通过Memecache@Facebook介绍了Facebook的Memcache架构,现在重新审视这个架构,仍有很多方面在业界保持先进性.作为weibo内部 ...

  3. Django push: Using Server-Sent Events and WebSocket with Django

    http://curella.org/blog/2012/jul/17/django-push-using-server-sent-events-and-websocket/ The goal of ...

  4. C++负数取模

    预习: r=余数 a=被除数 b=除数 c=商 a/b=c........r r=a-(a/b)*b 一.下面的题目你能全做对吗?1.7/4=?2.7/(-4)=?3.7%4=?4.7%(-4)=?5 ...

  5. JavaScript程序的执行顺序

    JavaScript程序的执行顺序:同步==>异步==>回调 同步是阻塞模式,异步是非阻塞模式.     同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个 ...

  6. java之jsp页面语法

    jsp页面相比静态页面html来说,就是多了一些脚本,利用这些脚本来动态地改变页面内容的显示. 1.JSP脚本写法 <% 这里写java代码; %> <%! JSP声明,用来声明变量 ...

  7. Maven打包详细流程

    方法一:cmd 控制台打包(比较不推荐) 首先安装maven插件百度下载一个,配置环境变量什么的~在cmd控制台能mvn version能有数据出现. 打包只需要到项目的根目录下~在cmd敲入mvn ...

  8. Java容器:Stack,Queue,PriorityQueue和BlockingQueue

    Stack Queue PriorityQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBlockingQueue ...

  9. cocos2d-x工作小记

    1.当一个layer跳到下一个layer时,需要传递数据,可以默认定义一个setUserData()方法. 2.cocos2d-x不使用传统的值类型,所有的对象都创建在堆上,然后通过指针引用. 3.传 ...

  10. FastDFS单机版安装

    FastDFS 分布式文件系统 1 目标 了解项目中使用FastDFS的原因和意义. 掌握FastDFS的架构组成部分,能说出tracker和storage的作用. 了解FastDFS+nginx上传 ...