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. css3中的布局相关样式

    web页面中的布局是指在页面中如何对标题.导航栏.主要内容.脚注.表单等各种构成要素进行合理编辑.在css3之前,主要使用float属性或position属性进行页面中的简单布局,但是也存在一些缺点, ...

  2. MySql 动态语句

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑. MyBatis中用于实现动态SQL的元素主要有: if choose(when,otherwise) ...

  3. Django 1.11 release note简明解读

    1.首先1.11这个版本是一个LTS版本 2.第一个支持python3.6的版本,最后一个支持python2.*的版本 3.Deprecating warnings 默认不再显示,同时建议第三方包开始 ...

  4. 神奇的namespace使用

    一大波概念正在来袭: 作用域与命名空间 相关概念 与命名空间相关的概念有:        声明域(declaration region)—— 声明标识符的区域.如在函数外面声明的全局变量,它的声明域为 ...

  5. [开源]基于ffmpeg和libvlc的视频剪辑、播放器

    [开源]基于ffmpeg和libvlc的视频剪辑.播放器 以前研究的时候,写过一个简单的基于VLC的视频播放器.后来因为各种项目,有时为了方便测试,等各种原因,陆续加了一些功能,现在集成了视频播放.视 ...

  6. Yii2表单提交(带文件上传)

    今天写一个php的表单提交接口,除了基本的字符串数据,还带文件上传,不用说前端form标签内应该有这些属性 <form enctype="multipart/form-data&quo ...

  7. Java中浮点数的精度问题 【转】

    当您在计算Money的时候,请看好了!!!要不损失了别后悔!!! 现象1: public static void main(String[] args) { System.out.println(0. ...

  8. 0513JS数组遍历、内置方法、训练

    一.定义一个数组把其中的偶数取出,组成一个新的数组 var attr = [9,34,80,27,56]; var attr1 = []; for(var i in attr){ if(attr[i] ...

  9. 读《图解HTTP》有感-(简单的HTTP协议)

    写在前面 该章节主要是针对HTTP1.1版本进行基础的讲解 正文 HTTP协议能做什么: http协议用于客户端和服务端之间的通信 HTTP协议通信方式: http协议是基于请求响应的方式来实现消息通 ...

  10. iframe跨域动态设置主窗口宽高

    Q:在A项目的a页面嵌入一个iframe,src是B项目的b页面,怎样让a页面的高度跟b页面的高度一样? A:解决跨域方案:增加一个A项目的c页面. 操作步骤: 一,a页面的iframe设置: 获取到 ...