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. rails关于user密码hash的重构

    rails应用程序中一个model名为User,其中存放了用户名和对应的密码.User模式类中建立了1个虚拟属性password用来存放用户实际输入的密码;而最终数据库的密码需要计算password的 ...

  2. React Native Android开发环境配置

    近些年,Web前端甚是火爆,火爆程度堪比我毕业时的移动端开发一般.随着JavaScript的火爆,移动端也不安分了起来,ReactNative就异军突起了,几乎要做到"一次学习,四处开发&q ...

  3. MySQL 和 JDBC(Java数据库连接)

    1.MySQL 1.1   MySQL简介 a)MySQL是一个开源免费的关系型数据库管理系统. b)默认用户:root c)默认端口号: 2.MySQL常用命令 2.1连接MySQL mysql   ...

  4. 02_Linux学习_命令

    帮助命令:        xxx --help        man xxx 列出当前目录下的目录和文件:        ls        ls -l        ls --help        ...

  5. 如何将程序集安装到全局程序集缓存GAC

    针对一些类库项目或用户控件项目(一般来说,这类项目最后编译生成的是一个或多个dll文件),在程序开发完成后,有时需要将开发的程序集(dll文件)安装部署到GAC(全局程序集缓存)中,以便其他的程序也可 ...

  6. Linux 下常用的Shell 命令

    英文原文链接:https://www.lopezferrando.com/30-interesting-shell-commands/ 1. 监控命令(每2秒运行一次) watch "ls ...

  7. Java SE学习笔记 --->高级类特性 ---> toString() 方法

    概述: toString() 方法在面向对象当中十分常见,使用频率很高,和equals() 方法一样,也是Object类中定义的方法. jdk中 源码: java.lang.Object类中ToStr ...

  8. MVC-AOP思想-Filter 三种注册方式

    在ASP.NET MVC框架中,为我们提供了四种类型的Filter类型包括:IAuthorizationFilter.IActionFilter.IResultFilter.IExceptionFil ...

  9. Redis linux 外部telnet访问不通

    外部访问不通: 1.修改redis.conf中的daemonize的值设为no: 2.修改redis.conf中的bind的值127.0.0.1为linux本身的ip地址,如192.168.1.120

  10. cxf webservice生成客户端代码及调用服务端遇到的问题

    1.  从网上下载cxf开发的工具 apache-cxf-3.1.4.zip, 解压文件,找到apache-cxf-3.1.4\bin目录,里面包含一个wsdl2java文件 2. 设置环境变量 1. ...