Go语言实现文件服务器
主调函数,设置路由表
package main
import (
"fmt"
"net/http"
"store/handler"
)
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)
http.HandleFunc("/file/delete", handler.FileDelHandler)
http.HandleFunc("/file/update", handler.FileMetaUpdateHandler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Printf("Failed to start server: %s", err.Error())
}
}
文件元信息
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) // 初始化
}
// UpdateFileMeta 更新文件元信息
func UpdateFileMeta(fileMeta FileMeta) {
fileMetas[fileMeta.FileSha1] = fileMeta
}
// GetFileMeta 获取文件元信息对象
func GetFileMeta(fileSha1 string) FileMeta {
return fileMetas[fileSha1]
}
// RemoveFileMeta 删除文件元信息
func RemoveFileMeta(fileSha1 string) {
delete(fileMetas, fileSha1)
}
handler逻辑
package handler
import (
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"store/meta"
"store/utils"
"time"
)
const storePath = "./tmp/"
func UploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// HTML 上传页面
data, err := ioutil.ReadFile("./static/view/index.html")
if err != nil {
_, _ = io.WriteString(w, "internal server error")
return
}
_, _ = io.WriteString(w, string(data))
} else if r.Method == "POST" {
// 接收文件流
file, head, err := r.FormFile("file")
if err != nil {
log.Printf("Failed to get data: %s\n", err.Error())
_, _ = io.WriteString(w, "")
return
}
defer file.Close()
fileMeta := meta.FileMeta{
FileName: head.Filename,
Location: storePath + head.Filename,
UploadAt: time.Now().Format("2006-01-02 15:04:05"),
}
newFile, err := os.Create(fileMeta.Location)
if err != nil {
log.Printf("Failed to create file: %s\n", err.Error())
return
}
defer newFile.Close()
_, err = io.Copy(newFile, file)
if err != nil {
log.Printf("Failed to save data into file: %s\n", err.Error())
return
}
_, _ = newFile.Seek(0, 0)
fileMeta.FileSha1 = utils.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 successfully")
}
// GetFileMetaHandler 获取文件元信息
func GetFileMetaHandler(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
fileHash := r.Form["filehash"][0]
fileMeta := meta.GetFileMeta(fileHash)
data, err := json.Marshal(fileMeta)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
_, _ = w.Write(data)
}
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
fileHash := r.Form.Get("filehash")
fileMeta := meta.GetFileMeta(fileHash)
file, err := os.Open(fileMeta.Location)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment;filename=\""+fileMeta.FileName+"\"")
_, _ = w.Write(data)
}
func FileDelHandler(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
fileHash := r.Form.Get("fileHash")
fileMeta := meta.GetFileMeta(fileHash)
_ = os.Remove(fileMeta.Location)
meta.RemoveFileMeta(fileHash)
w.WriteHeader(http.StatusOK)
}
func FileMetaUpdateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
_ = r.ParseForm()
opType := r.Form.Get("op")
if opType != "0" {
w.WriteHeader(http.StatusForbidden)
return
}
fileHash := r.Form.Get("filehash")
newFileName := r.Form.Get("filename")
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)
}
加密
package utils
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"hash"
"io"
"os"
"path/filepath"
)
type Sha1Stream struct {
_sha1 hash.Hash
}
func (obj *Sha1Stream) Update(data []byte) {
if obj._sha1 == nil {
obj._sha1 = sha1.New()
}
obj._sha1.Write(data)
}
func (obj *Sha1Stream) Sum() string {
return hex.EncodeToString(obj._sha1.Sum([]byte("")))
}
func Sha1(data []byte) string {
_sha1 := sha1.New()
_sha1.Write(data)
return hex.EncodeToString(_sha1.Sum([]byte("")))
}
func FileSha1(file *os.File) string {
_sha1 := sha1.New()
_, _ = io.Copy(_sha1, file)
return hex.EncodeToString(_sha1.Sum(nil))
}
func MD5(data []byte) string {
_md5 := md5.New()
_md5.Write(data)
return hex.EncodeToString(_md5.Sum([]byte("")))
}
func FileMD5(file *os.File) string {
_md5 := md5.New()
_, _ = io.Copy(_md5, file)
return hex.EncodeToString(_md5.Sum(nil))
}
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
func GetFileSize(filename string) int64 {
var result int64
_ = filepath.Walk(filename, func(path string, f os.FileInfo, err error) error {
result = f.Size()
return nil
})
return result
}
前端:
<html lang="">
<head>
<!-- bootstrap 4.x is supported. You can also use the bootstrap css 3.3.x versions -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/css/fileinput.min.css" media="all"
rel="stylesheet" type="text/css" />
<!-- if using RTL (Right-To-Left) orientation, load the RTL CSS file after fileinput.css by uncommenting below -->
<!-- link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/css/fileinput-rtl.min.css" media="all" rel="stylesheet" type="text/css" /-->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- piexif.min.js is needed for auto orienting image files OR when restoring exif data in resized images and when you
wish to resize images before upload. This must be loaded before fileinput.min.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/js/plugins/piexif.min.js"
type="text/javascript"></script>
<!-- sortable.min.js is only needed if you wish to sort / rearrange files in initial preview.
This must be loaded before fileinput.min.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/js/plugins/sortable.min.js"
type="text/javascript"></script>
<!-- purify.min.js is only needed if you wish to purify HTML content in your preview for
HTML files. This must be loaded before fileinput.min.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/js/plugins/purify.min.js"
type="text/javascript"></script>
<!-- popper.min.js below is needed if you use bootstrap 4.x. You can also use the bootstrap js
3.3.x versions without popper.min.js. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script>
<!-- bootstrap.min.js below is needed if you wish to zoom and preview file content in a detail modal
dialog. bootstrap 4.x is supported. You can also use the bootstrap js 3.3.x versions. -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js"
type="text/javascript"></script>
<!-- the main fileinput plugin file -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/js/fileinput.min.js"></script>
<!-- optionally if you need a theme like font awesome theme you can include it as mentioned below -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/themes/fa/theme.js"></script>
<!-- optionally if you need translation for your language then include locale file as mentioned below -->
<title></title>
</head>
<body style="width:100%;height:100%;text-align:center;">
<div style="width:60%;height:30%;text-align:center;">
<form action='#' method="post" enctype="multipart/form-data">
<input id="file" name="file" type="file" class="file" data-msg-placeholder="选择文件">
</form>
</div>
</body>
</html>
目录结构
.
├── go.mod
├── handler
│ └── handler.go
├── main.go
├── meta
│ └── file.go
├── static
│ └── view
│ ├── index.html
│ └── search.html
├── tmp
│ └── apple.jpg
└── utils
└── utils.go
Go语言实现文件服务器的更多相关文章
- JVM 平台上的各种语言的开发指南
JVM 平台上的各种语言的开发指南 为什么我们需要如此多的JVM语言? 在2013年你可以有50中JVM语言的选择来用于你的下一个项目.尽管你可以说出一大打的名字,你会准备为你的下一个项目选择一种新的 ...
- Ueditor1.4.3实现跨域上传到独立文件服务器,完美解决单文件和多文件上传!
再写配置方法之前先吐槽一下网上的各种教程,TM没一个有卵用,一群傻屌不会写就别写,写了就要负责. 百度google搜了半天,全是配置什么document.domain,根域名什么的,我只想对你说: 好 ...
- Java语言进阶过程(转)
[以下肯定是不完整的列表,欢迎补充] Java是一个通用的编程语言,其实可以干很多事,怎么学Java就看怎么用了. 但有一些一般的步骤: 1. 熟悉一种文本编辑器,比如Vim, Emacs, Note ...
- 无敌简单快速的文件服务器sgfs
前言 想要构建一个Linux文件服务器?看看下面几个要求是不是你想要的? 1.只需要单节点部署就够了 2.部署启动简单,下载之后,一键启动,一键关闭 3.不需要任何其他的依赖安装,而且运行时占用内存资 ...
- linux搭建FastDFS文件服务器
本文主要介绍在linux服务器如何搭建FastDFS文件服务器.大概分为9个步骤,由于内容较为繁琐.下面带你入坑! 首先简单介绍一下FastDFS是淘宝资深架构师余庆老师主导开源的一个分布式文件系统, ...
- 不安分的 Go 语言开始入侵 Web 前端领域了!( WebAssembly )
参考:https://blog.csdn.net/csdnnews/article/details/84038848 从 Go 语言诞生以来,它就开始不断侵蚀 Java .C.C++ 语言的领地.今年 ...
- 百度富文本编辑器整合fastdfs文件服务器上传
技术:springboot+maven+ueditor 概述 百度富文本整合fastdfs文件服务器上传 详细 代码下载:http://www.demodashi.com/demo/15008.h ...
- 开源文件服务器file-service介绍
file-service 一个基于ASP.NET Core的可伸缩.通用的文件服务器. 通常后端项目可能会有头像.图片.音频.视频等上传/下载需求,这些需求都可以抽象为文件服务. 功能特点 支持Lin ...
- Golang vs PHP 之文件服务器
前面的话 作者为golang脑残粉,本篇内容可能会引起phper不适,请慎读! 前两天有同事遇到一个问题,需要一个能支持上传.下载功能的HTTP服务器做一个数据中心.我刚好弄过,于是答应帮他搭一个. ...
- Go语言编程 (许式伟 等 著)
第1章 初识Go语言 1.1 语言简史 1.2 语言特性 1.2.1 自动垃圾回收 1.2.2 更丰富的内置类型 1.2.3 函数多返回值 1.2.4 错误处理 1.2.5 匿名函数和闭包 1.2.6 ...
随机推荐
- Java发送http请求携带token,使用org.nutz
发送http请求,需要携带token数据,创建Header传输 Header header = Header.create(); header.set("Authorization" ...
- SQL Server 手工 锁表、查询被锁表、解锁相关语句
SQL Server 手工 锁表.查询被锁表.解锁相关语句 --锁表(其它事务不能读.更新.删除) BEGIN TRAN SELECT * FROM <表名> WITH(TABLOCKX) ...
- C++ vector的emplace_back函数
C++ STL的vector相信大家一定都知道,它是一个一般用来当做可变长度列表的类.在C++11之前,一般给vector插入新元素用得都是push_back函数,比如下面这样: std::vecto ...
- pretty break
scale_x_continuous( breaks = pretty_breaks(10),labels=scales::comma)+ x <- 1:4 y <- c(0, 0.000 ...
- 深入理解css 笔记(7)
前面讲了几种控制网页布局的方式,flex,gird 和 float.这下我们初略讲下 position.这个我日常中用到的已经挺多了.定位和其他控制文档流的行为不同.它将元素彻底从文档流中移走,它 ...
- Sql Server代理作业、定时任务
需求: 本次需求为每15分钟获取一次思路为,创建结果表,代理作业定时更新数据并存入结果表,后端只需要调用结果表数据数据,如果超期不同的天数则给出不同的提示信息,因为没有触发点,所以用到了本文内容. 右 ...
- Jackson工具类及其配置
1 package com.ruoyi.common.core.utils.json; 2 3 import com.fasterxml.jackson.annotation.JsonAutoDete ...
- Istio思考往前一小步~系列一
思考起源于现实应用需求,随着微服务理念普及,基础设施从单机到容器到Kubernetes,体验过集群的各种好处之后,我们还缺少什么?为什么还要在kubernetes的基础上部署Istio?个人认为Ist ...
- 西电oj 244题单词排序
题目描述: 定义一个二维字符数组str[10][20],行号表示单词序号,列号表示单词最大长度, 输入一个正整数N(N≤10),表示单词数,使用函数wd_sort()完成单词的排序,按字母顺序从小到大 ...
- R 字符串操作超全总结
paste函数和paste0()函数 连接字符 > paste("a", 1:3) #默认空格符连接,即sep=" " [1] "a 1&quo ...