zoukankan      html  css  js  c++  java
  • 实现一个网盘存储……

    前言

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

    什么语言?

    开始想用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)
    }
    ```
    
    以上就是简单版的文件上传服务
  • 相关阅读:
    Apache并发相关的几个配置
    MPM:Multi-Processing Modules
    regsvr32是干什么的
    第一个子容器设置margin-top后和父容器发生外边距合并,解决办法是给父容器设置非0 padding-top或overflow:hidden
    li或dd 浮动后增加图片时高度多出3-5px的问题
    [Error: No platforms added to this project. Please use `cordova platform add <platform>`.]
    单链表的实现(python)
    python的顺序表(list,tuple)
    python装饰器
    梯度下降算法
  • 原文地址:https://www.cnblogs.com/zhoulixiansen/p/12640080.html
Copyright © 2011-2022 走看看