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. obj-c编程17:键值观察(KVO)

    说完了前面一篇KVC,不能不说说它的应用KVO(Key-Value Observing)喽.KVO类似于ruby里的hook功能,就是当一个对象属性发生变化时,观察者可以跟踪变化,进而观察或是修正这个 ...

  2. java多继承

    众所周知,java面向对象语言中只有单继承的编程语言,也许你会说,通过实现多个接口这种变通的方式达到多继承的目的.没错,你说的对,不过这并不是本片文章要说到的内容,本文要讲到的内容是java中实实在在 ...

  3. Course2-Python函数和模块

    一. 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率. 上一课里提到了Python的很多内置函数.在此主要讲自定义函数. 1. 定 ...

  4. [转]web服务器压力测试工具

    http_load学习心得: 测试网站每秒所能承受的平均访问量(吞吐量) http_load -parallel 5 -fetches 1000 urls.txt这段命令行是同时使用5个进程,随机访问 ...

  5. (转) windows下 安装 rabbitMQ 及操作常用命令

    该博客转载自:https://blog.csdn.net/gy__my/article/details/78295943 原作者:Eric Li  出处:http://www.cnblogs.com/ ...

  6. Angular集成UEditor

    1.Ueditor的集成主要通过把UEditor做成一个Component来实现,先上Component代码: import { AfterContentInit, Component, Input, ...

  7. Ubuntu下定时任务和自启动任务的部署

    1.定时任务的部署,最简单的方法是执行 crontab -e 然后在下面加上世间周期配置和要执行的命令,一般情况下,可以把要执行的任务用bash脚本封装一下,格式如下所示: minute   hour ...

  8. c#与webapi交互

    public static string HttpConnectToServer(string ServerPage,string strData) { string postData =strDat ...

  9. 设计模式的征途—22.中介者(Mediator)模式

    我们都用过QQ,它有两种聊天方式:一是私聊,二是群聊.使用QQ群,一个用户就可以向多个用户发送相同的信息和文件,从而无需一一发送,节省大量时间.通过引入群的机制,极大地减少系统中用户之间的两两通信,用 ...

  10. Python_字符串简单加密解密

    def crypt(source,key): from itertools import cycle result='' temp=cycle(key) for ch in source: resul ...