前言
最近公司弃用企业级的某盘,因为资料泄露的缘故,让我我搞一个类似的东西供内部使用,所以就一边写博客一边写思路吧
什么语言?
开始想用Python来写,使用Django框架,但是觉得有些臃肿,并且上传文件等涉及到io操作,大量用户觉得不太行,索性就用go来写
先使用go mod 告别GOPATH
main.go
package main
import (
"filestore-server/handler"
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/file/upload", handler.UploadHandler)
http.HandleFunc("/file/upload/suc", handler.UploadSucHandler)
err := http.ListenAndServe("localhost:8080", nil)
if err != nil {
fmt.Printf("Failed reseaon %s", err)
}
}
路由对应的视图函数位置handler.go
package handler
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
)
// 处理文件上传
func UploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// 返回上传html页面
if data, err := ioutil.ReadFile("./static/view/index.html"); err != nil {
io.WriteString(w, "error of read file")
return
} else {
io.WriteString(w, string(data))
}
} else if r.Method == "POST" {
// 接收文件流及存储到本地目录
file, head, err := r.FormFile("file")
if err !=nil {
fmt.Printf("Failed to get data,err:%s
", err)
return
}
defer file.Close()
newFile, err := os.Create("/Users/jack_zhou/coding_test" + head.Filename)
if err != nil {
fmt.Printf("Filed to create file")
return
}
defer newFile.Close()
_, err = io.Copy(newFile, file) // 忽略返回的字节长度
if err != nil {
fmt.Printf("Failed to save data into file, err%s", err.Error())
http.Error(w, err.Error(),http.StatusInternalServerError)
return
}
http.Redirect(w,r,"/file/upload/suc", http.StatusFound)
}
}
// 上传已完成
func UploadSucHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Upload finished")
}
先看一下效果
速度简直是秒传啊
(更新与4月4日晚11.59)
package meta
// FileMeta: 文件信息结构
type FileMeta struct {
FileSha1 string
FileName string
FileSize int64
Location string
UploadAt string
}
var fileMetas map[string]FileMeta
func init() {
fileMetas = make(map[string]FileMeta)
}
// 新增或者更新文件元信息
func UpdateFileMeta(fmeta FileMeta) {
fileMetas[fmeta.FileSha1] = fmeta
}
// 通过sha1值获取文件元信息
func GetFileMeta(fileSha1 string) FileMeta {
return fileMetas[fileSha1]
}
对文件信息存储的结构体
包含:哈希,名称,大小,存储位置,上传时间。
那么对原有的逻辑进行修改
handler.go
package handler
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"filestore-server/meta"
"filestore-server/util"
"time"
)
// 处理文件上传
func UploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// 返回上传html页面
if data, err := ioutil.ReadFile("./static/view/index.html"); err != nil {
io.WriteString(w, "error of read file")
return
} else {
io.WriteString(w, string(data))
}
} else if r.Method == "POST" {
// 接收文件流及存储到本地目录
file, head, err := r.FormFile("file")
if err !=nil {
fmt.Printf("Failed to get data,err:%s
", err)
return
}
defer file.Close()
filemeta := meta.FileMeta{
FileName:head.Filename,
Location:"/Users/jack_zhou/coding_test" + 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")
return
}
defer newFile.Close()
filemeta.FileSize, err = io.Copy(newFile, file) // 第一个返回值是文件的大小
if err != nil {
fmt.Printf("Failed to save data into file, err%s", err.Error())
http.Error(w, err.Error(),http.StatusInternalServerError)
return
}
newFile.Seek(0,0) // 因为之前拷贝了,所以将游标变成第一个位置
filemeta.FileSha1 = util.FileSha1(newFile)
meta.UpdateFileMeta(filemeta)
http.Redirect(w,r,"/file/upload/suc", http.StatusFound)
}
}
// 上传已完成
func UploadSucHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Upload finished")
}
```
关于go的IO操作的用法,详细可以看我的博客(Go的IO操作)
昨天感冒了,躺了一天
写一个关于查询文件的接口
```go
// 获取文件元信息的接口
func GetFileMetaHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // 必要的判断
filehash := r.Form["filehash"][0]
fMeta := meta.GetFileMeta(filehash)
data, err := json.Marshal(fMeta)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(data)
}
```
下载接口
```go
// 提供文件下载
func DownloadHandler(w http.ResponseWriter, r *http.Request){
r.ParseForm()// 解析参数
filehash := r.Form.Get("filehash")
fm := meta.GetFileMeta(filehash)
f, err := os.Open(fm.Location)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
defer f.Close()
data, err := ioutil.ReadAll(f) // 读到内存
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octect-stream")
w.Header().Set("Content-Descrption", "attachment;filement="" + fm.FileName + """)
w.Write(data)
}
```
更新文件名
```go
// 更新文件
func FileMetaUpdateHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
opType := r.Form.Get("op")
filehash := r.Form.Get("filehash")
newfilename := r.Form.Get("filename")
if opType != "0" {
w.WriteHeader(http.StatusForbidden)
return
}
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
curFileMeta := meta.GetFileMeta(filehash)
curFileMeta.FileName = newfilename
meta.UpdateFileMeta(curFileMeta)
data, err := json.Marshal(curFileMeta)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write(data)
}
```
删除文件
```go
// 删除文件
func FileDelHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
filehash := r.Form.Get("filehash")
fm := meta.GetFileMeta(filehash)
meta.RemoveFileMeta(filehash)
os.Remove(fm.Location)
w.WriteHeader(http.StatusOK)
}
```
以上就是简单版的文件上传服务