# 接口列表
接口描述 | 接口 URL |
---|---|
文件上传接口 | POST:/file/upload |
文件查询接口 | GET:/file/meta |
文件下载接口 | GET:/file/download |
文件重命名接口 | POST:/file/update |
文件删除接口 | POST:/file/delete |
项目源码
1 | 链接:https://pan.baidu.com/s/1Yl7kDSdwSmi9m45JDqnftg?pwd=a79t |
# 上传接口
- 获取上传页面
- 选取本地文件,form 形式上传文件
- 云端接受文件流,写入本地存储
- 云端更新文件元信息集合
# 项目文件
1 | - main.go |
main.go
中主要是建立路由,即访问路径与事务函数的绑定。端口监听。handler.go
定义事务函数filemeta.go
定义文件元信息的结构体,目的是方便查询。util.go
生成 hash 码、加密的工具
# demo 分析
main.go
- 通过
import
引入定义在 handler 里的事务函数,以及所需的 http 包,因为路由规则的绑定是由 http 包里的http.HandleFunc()
http.HandleFunc()
需要传入两个参数。第一个是字符型的 url 访问路径,第二个是对应的功能函数。- 还要进行端口监听,
http.ListenAndServe(":8080", nil)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import (
"filestore-server/handler"
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/file/upload", handler.UploadHandler) //建立路由规则
http.HandleFunc("/file/upload/suc", handler.UploadSucHandler)
http.HandleFunc("/file/meta", handler.GetFileMetaHandler)
http.HandleFunc("/file/download", handler.DownloadHandler)
err := http.ListenAndServe(":8080", nil) //端口监听
if err != nil {
fmt.Printf("Failed to start server,err:%s", err)
}
}
- 通过
handler.go
- 这里是处理请求的核心函数,主要通过
net/http
包处理网页请求的处理,通过io/ioutil
完成文件流的处理。 - 每个函数都有两个参数,
func UploadHandler(writer http.ResponseWriter, request *http.Request)
。writer:http.ResponseWriter
向用户返回数据的对象request: *http.Request
接受用户请求的对象指针
- 逻辑层面:主要分为
GET
和POST
方式GET
是为了第一次访问时,返回文件上传页面。挺有意思的是往writer
里写的是一个字符串。POST
,文件上传是以 post 的方式上传的,所以判断,如果请求方式是POST
则进行文件存储逻辑。
- 文件存储逻辑
- 首先从请求表单中获取文件流,利用
request.FormFile("file")
方法,里面的字符串是上传时给文件流的一个 key。记得用 defer 关闭 - 创建新的文件路径以保存上传的文件,
newFile, err := os.Create("./tmp/" + head.Filename)
- 使用
io.Copy(newFile, file)
将上传的文件复制到本地 - 返回一个重定向页面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102package handler
import (
"encoding/json"
"filestore-server/meta"
"filestore-server/util"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
)
// writer:http.ResponseWriter 向用户返回数据的对象
// request: http.Request 接受用户请求的对象指针
func UploadHandler(writer http.ResponseWriter, request *http.Request) {
if request.Method == "GET" {
//返回上传的html页面
data, err := ioutil.ReadFile("./static/view/index.html")
if err != nil {
io.WriteString(writer, "load html error")
return
}
io.WriteString(writer, string(data))
} else if request.Method == "POST" {
//接受文件流及存储到本地目录
file, head, err := request.FormFile("file") //通过表单
if err != nil {
fmt.Printf("Failed to get file,error:%s\n", err.Error())
return
}
defer file.Close() //使用defer在函数退出之前关闭资源
// 元信息初始化
fileMeta := meta.FileMeta{
FileName: head.Filename,
Location: "./tmp/" + head.Filename,
UploadAt: time.Now().Format("2006-01-02 15:04:05"),
}
newFile, err := os.Create(fileMeta.Location) //创建文件流,空的
if err != nil {
fmt.Printf("Filed to create file,err:%s\n", err.Error())
return
}
defer newFile.Close()
//数据复制
fileMeta.FileSize, err = io.Copy(newFile, file)
if err != nil {
fmt.Printf("Filed to save file,err:%s\n", err.Error())
return
}
//移动file句柄
newFile.Seek(0, 0)
fileMeta.FileSha1 = util.FileSha1(newFile)
fmt.Print(fileMeta.FileSha1)
meta.UpdateFileMeta(fileMeta) //加入到hash桶
http.Redirect(writer, request, "/file/upload/suc", http.StatusFound)
}
}
// 上传已完成
func UploadSucHandler(writer http.ResponseWriter, request *http.Request) {
io.WriteString(writer, "Upload Succeed")
}
// GetFileMetaHandler获取文件元信息
func GetFileMetaHandler(writer http.ResponseWriter, request *http.Request) {
request.ParseForm() //解析表单
filehash := request.Form["filehash"][0] //根据表单中的key获取对应数据,获取到的数据是一个数组,但是数组只有一个元素,所以取第一个就好
fMeta := meta.GetFileMeta(filehash)
data, err := json.Marshal(fMeta) //将结构体转换为json
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
//http.StatusInternalServerError untyped int = 500
}
writer.Write(data)
}
func DownloadHandler(writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
filehash := request.Form.Get("filehash")
filemeta := meta.GetFileMeta(filehash)
f, err := os.Open(filemeta.Location) //return *os.File
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}
data, err := ioutil.ReadAll(f) //return []byte
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
}
writer.Header().Set("Content-Type", "application/octect-stream") //
writer.Header().Set("content-disposition", "attachment;filename=\""+filemeta.FileName+"\"")
writer.Write(data) //如果是移动端,传输byte数组即可。但网页端需要设置响应头
}
- 首先从请求表单中获取文件流,利用
- 这里是处理请求的核心函数,主要通过
# 文件查询
文件的唯一标识,hash 码
因此查询文件时可以通过文件 hash 获取 meta 信息
文件的关键信息有:文件唯一标识符,文件名,保存路径,存储时间
filemeta.go
- 包含元信息结构体的定义,一个存储结构体的
map(key=hashcode,value=filemeta)
, 初始化结构体函数 (init
) 以及两个接口函数init()
:init 函数,先于 main 函数执行,提前将 fileMetas map 初始化,用于存储信息。UpdateFileMeta
:filemeta 更新;新增或者更新文件元信息GetFileMeta
:通过 hash code 获取文件的元信息对象
- 代码如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package meta
//文件元信息结构
type FileMeta struct {
FileSha1 string //文件唯一标志
FileName string
FileSize int64
Location string
UploadAt string
}
var fileMetas map[string]FileMeta //key:hash code ; value: filemeta
//初始化:
func init() {
fileMetas = make(map[string]FileMeta)
}
//接口:filemeta更新;新增或者更新文件元信息
func UpdateFileMeta(fmeta FileMeta) {
fileMetas[fmeta.FileSha1] = fmeta
}
//通过hash code获取文件的元信息对象
func GetFileMeta(fileSha1 string) FileMeta {
return fileMetas[fileSha1]
}
- 包含元信息结构体的定义,一个存储结构体的
在 handler.go
对应的函数
- 返回的是元信息的结构体数据。
1
2
3
4
5
6
7
8
9
10
11
12
13// GetFileMetaHandler获取文件元信息
func GetFileMetaHandler(writer http.ResponseWriter, request *http.Request) {
request.ParseForm() //解析表单
filehash := request.Form["filehash"][0] //根据表单中的key获取对应数据,获取到的数据是一个数组,但是数组只有一个元素,所以取第一个就好
fMeta := meta.GetFileMeta(filehash)
data, err := json.Marshal(fMeta) //将结构体转换为json
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
//http.StatusInternalServerError untyped int = 500
}
writer.Write(data)
}
# 文件下载
逻辑:前端通过 url 传输想要下载的文件的 hashcode ( http://localhost:8080/file/download?filehash=2cf273073890ad63c214a6565bfe981713f4e3
),DownloadHandler 解析 url 信息并获取到 hash code,在 filemeta map 中根据 hash 码查询对应文件元信息,根据元信息中的路径打开并读取文件 ( [] byte
),最后将读取到的文件返回给客户端。
1 | func DownloadHandler(writer http.ResponseWriter, request *http.Request) { |
# 文件删除
文件删除包含两个删除
- 删除文件元信息
- 物理删除
# 删除文件元信息
该部分需要在 filemeta 下定义一个删除函数
1 | //删除元信息 |
# 物理删除
1 | func FileDeleteHandler(writer http.ResponseWriter, request *http.Request) { |