简单需要一个文件服务器来传递数据,只要两个功能,一个上传接口,一个下载接口。
选用go http模块实现,比nginx、ftp等更方便快捷。
需求整理- 上传接口"/v1/file_upload/"
- 上传接口增加简单BasicAuth鉴权
- 上传成功返回下载URL
json格式返回
只想文件上传服务器测试接口,以下电梯直达即可
CSDN 5积分下载
白嫖
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
)
type ViewFunc func(http.ResponseWriter, *http.Request)
func http_resp(code int, msg string, w http.ResponseWriter) {
var Result map[string]interface{}
Result = make(map[string]interface{})
Result["code"] = code
Result["msg"] = msg
data, err := json.Marshal(Result)
if err != nil {
log.Printf("%v\n", err)
}
w.Write([]byte(string(data)))
}
func upload(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, _ := template.ParseFiles("upload.html")
t.Execute(w, nil)
return
}
contentType := r.Header.Get("content-type")
contentLen := r.ContentLength
if !strings.Contains(contentType, "multipart/form-data") {
http_resp(-1001, "The content-type must be multipart/form-data", w)
return
}
//限制最大文件大小
if contentLen >= 50*1024*1024 {
http_resp(-1002, "Failed to large,limit 50MB", w)
return
}
err := r.ParseMultipartForm(50 * 1024 * 1024)
if err != nil {
http_resp(-1003, "Failed to ParseMultipartForm", w)
return
}
if len(r.MultipartForm.File) == 0 {
http_resp(-1004, "File is NULL", w)
return
}
var Result map[string]interface{}
Result = make(map[string]interface{})
Result["code"] = 0
DownLoadUrl := "http://192.168.10.9:8080/"
FileCount := 0
for _, headers := range r.MultipartForm.File {
for _, header := range headers {
log.Printf("recv file: %s\n", header.Filename)
filePath := filepath.Join("./upload", header.Filename)
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Printf("Create file %s error: %s\n", filePath, err)
return
}
srcFile, err := header.Open()
if err != nil {
log.Printf("Open header failed: %s\n", err)
}
_, err = io.Copy(dstFile, srcFile)
if err != nil {
log.Printf("Write file %s error: %s\n", filePath, err)
}
_, _ = srcFile.Close(), dstFile.Close()
FileCount++
name := fmt.Sprintf("file%d_url", FileCount)
Result[name] = (DownLoadUrl + header.Filename)
}
}
data, err := json.Marshal(Result)
if err != nil {
log.Printf("%v \n", err)
}
w.Write([]byte(string(data)))
}
func BasicAuth(f ViewFunc, user, passwd []byte) ViewFunc {
return func(w http.ResponseWriter, r *http.Request) {
basicAuthPrefix := "Basic "
// 获取 request header
auth := r.Header.Get("Authorization")
// 如果是 http basic auth
if strings.HasPrefix(auth, basicAuthPrefix) {
// 解码认证信息
payload, err := base64.StdEncoding.DecodeString(
auth[len(basicAuthPrefix):],
)
if err == nil {
pair := bytes.SplitN(payload, []byte(":"), 2)
if len(pair) == 2 && bytes.Equal(pair[0], user) &&
bytes.Equal(pair[1], passwd) {
// 执行被装饰的函数
f(w, r)
return
}
}
}
// 认证失败,提示 401 Unauthorized
// Restricted 可以改成其他的值
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
// 401 状态码
w.WriteHeader(http.StatusUnauthorized)
}
}
func main() {
addr := flag.String("addr", "0.0.0.0:8080", "服务绑定地址端口")
user := flag.String("user", "", "http认证用户名(如果用户名为空,则不需要认证)")
psw := flag.String("psw", "", "http认证密码")
root := flag.String("root", "./upload/", "文件服务根目录")
flag.Parse()
os.Mkdir(*root, 0777)
http_user := []byte(*user)
http_psw := []byte(*psw)
log.Printf("http server listen on %v \n", *addr)
if *user != "" {
http.HandleFunc("/v1/file_upload", BasicAuth(upload, http_user, http_psw))
} else {
http.HandleFunc("/v1/file_upload", upload)
}
http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(*root))))
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Printf("%v", err)
}
}
网页上传
新建文件upload.html放到http服务器root目录。例如upload文件夹
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>go 上传test</title>
</head>
<body>
<form method="POST" action="/v1/file_upload" enctype="multipart/form-data" >
<input type="file" name="uploadfile" />
<input type="submit" value="上传">
</form>
</body>
</html>