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 ...
随机推荐
- 如何利用 C# + Echarts 绘制 Bar Simple
背景 Echarts 是百度推出的一个使用 JavaScript 实现的开源可视化库. 该库提供了常规的折线图.柱状图.散点图.饼图.K线图,用于统计的盒形图,用于地理数据可视化的地图.热力图.线图, ...
- JS实现10进制和26进制的转换
转载:https://blog.csdn.net/quentain/article/details/52803891 //将26进制转10进制 var ConvertNum = function (s ...
- Windows平台在当前文件夹下打开CMD
Windows平台在当前文件夹下打开CMD的方法: 在路径栏中输入 cmd.exe ,然后敲回车.
- C#中冒号:使用场景
1. 继承类.实现接口与继承构造函数 ① 继承类 /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public p ...
- node+appium安装
node是什么: node全称Node.js,是一个基于Chrome V8引擎的JavaScript运行环境,一个让JavaScript 运行在服务端的开发平台:它让JavaScript成为与PH ...
- uni-app使用Sqlite
step2:封装常用操作(未对事务进行封装 HTML5+ API Reference (html5plus.org)) // //打开数据库 function openDb(name,path) { ...
- File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent
IDEA编译报以下错误 File encoding has not been set, using platform encoding UTF-8, i.e. build is platform de ...
- 理解Map-构建关联数组
理解Map 要更深入理解map,学习如何构建关联数组是很有帮助的,以下是简单实现 package org.example.onjava.senior.example03collection.map; ...
- PHP 计算机码、位运算、运算符优先级
计算机码 计算机在实际存储数据的时候,是采用编码规则的(二进制编码) 计算机码存储的过程: 原码.反码和补码,数值最左边一位用来充当符号位:符号为正数为0,负数为1 原码:数据本身从十进制转换为二进制 ...
- AC自动机模板题 HDU - 2222
Keywords Search HDU - 2222 贴个vj的链接https://vjudge.net/problem/HDU-2222 题意:T组数据,n个单词,再给你一个串,看有几个单词在这个 ...