前言

最近公司弃用企业级的某盘,因为资料泄露的缘故,让我我搞一个类似的东西供内部使用,所以就一边写博客一边写思路吧

什么语言?

开始想用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\n", 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\n", 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)
}
```

以上就是简单版的文件上传服务
posted on 2020-04-06 00:00  人生苦短use,what?  阅读(234)  评论(0编辑  收藏  举报