基于Go语言的网盘开发(GloudDisk)

(记录一下自己做项目的过程) 基于go-zero实现的简易的网盘系统,如果有小伙伴对这个项目感兴趣,可以去网上搜索一些资料。这里推荐一下我学习的来源:【项目实战】基于Go-zero、Xorm的网盘系统_哔哩哔哩_bilibili 

确定功能模块:

目录

 xorm连接数据库并创建表结构

 集成go—zero

一、用户模块

(1)密码登录

(2)邮箱注册

 (3)个人资料详情

二、存储池模块

(1)中心存储池资源管理

①文件上传

(2)个人存储池资源管理 

①文件关联存储

②用户文件列表 

③用户文件名称修改

④用户文件夹创建

⑤用户文件删除

⑥用户文件移动

三、文件共享模块

(1)文件分享

①创建分享记录

②获取资源详情

③资源保存


 xorm连接数据库并创建表结构

 创建项目--安装xorm( go get xorm.io/xorm)

文档地址:Xorm

连接数据库:

xorm.NewEngine("mysql", "root:123@/test?charset=utf8")

先创建UserBasic结构体,为了之后的测试,如下:(新建models文件夹,在文件夹下新建user_basic.go)

package Models

type UserBasic struct {
	ID int
	Identity string
	Name string
	Password string
	Email string
}

func (table UserBasic) TableName() string {
	return "user_basic"
}

之后通过 engine.CreateTables(),参数为一个或多个空的对应Struct的指针,创建数据表,具体操作如下:(为了测试,新建一个文件夹test,新建xorm_test.go)

package test

import (
	"CloudDisk/Models"
	bytes2 "bytes"
	"encoding/json"
	_ "github.com/go-sql-driver/mysql"
	"testing"
	"xorm.io/xorm"
)

func TestXorm(t *testing.T) {
	engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
	if err!=nil {
		t.Fatal(err)
	}
	TableUser:=make([]*Models.UserBasic,0)
	m := new(Models.UserBasic)
	err = engine.CreateTables(m)
	if err!=nil {
		t.Fatal(err)
	}
	err = engine.Find(&TableUser)//这里查出来的是地址
	if err!=nil {
		t.Fatal(err)
	}
	//将以json格式输出
	bytes, err:= json.Marshal(TableUser)//转换成BYTE数组
	if err!=nil {
		t.Fatal(err)
	}
	dst := new(bytes2.Buffer)
	err = json.Indent(dst, bytes, "", "  ") //转换为buffer
	if err!=nil {
		t.Fatal(err)
	}
	println(dst.String())
}

 集成go—zero

文档地址:简介 · go-zero document

 安装:

go get -u github.com/zeromicro/go-zero

因为项目比较小,所以直接选择单体服务就可以了,

安装Goctl,完成后执行 goctl -v,如果输出版本信息则代表安装成功

# Go 1.15 及之前版本
go get -u github.com/zeromicro/go-zero/tools/goctl@latest

# Go 1.16 及以后版本
go install github.com/zeromicro/go-zero/tools/goctl@latest

创建greet服务,如下图,因为我们 mkdircd 都已经完成了,所以直接  goctl api new [可自定义名字] (我的是goctl api new code,因为下面我会直接说code文件,所以希望大家不要迷糊我这code是啥了)

基于Go语言的网盘开发(GloudDisk)_第1张图片 之后启动服务:

$ cd greet
$ go run greet.go -f etc/greet-api.yaml

之后通过http://localhost:8888/from/you来访问服务,如下图:

基于Go语言的网盘开发(GloudDisk)_第2张图片 可以看到当前的返回值为null,那是因为我们在 logic 文件里的 .go文件 没有写任何的业务逻辑(我的名字是code,所以这里是codelogic.go),如下图:

基于Go语言的网盘开发(GloudDisk)_第3张图片

如果我们改成 基于Go语言的网盘开发(GloudDisk)_第4张图片

重新启动服务,会出现这样的页面:

 基于Go语言的网盘开发(GloudDisk)_第5张图片

但是为什么返回的是Message呢?因为返回的response里面定义的是一个Message集。在code.api中可以看到,如下图:基于Go语言的网盘开发(GloudDisk)_第6张图片 

 以后我们就可以在这里写业务逻辑了。

接下来我们来整合一下,将models文件放入code目录下,并在models中创建init.go用来连接数据库。如下:

基于Go语言的网盘开发(GloudDisk)_第7张图片

 代码:

package Models

import (
	"log"
	"xorm.io/xorm"
	_ "github.com/go-sql-driver/mysql"
)
var Engine=Init()
func Init() *xorm.Engine {
	engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
	if err!=nil {
		log.Println("engine Error:",err)//log包的println会自动return
	}
	return engine
}

然后在 codelogic.go 进行业务逻辑的实现: (其实就是将文件 xorm_test.go 中的代码复制过来进行测试)

package logic

import (
	"CloudDisk/code/Models"
	bytes2 "bytes"
	"context"
	"encoding/json"
	"log"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"
	"github.com/zeromicro/go-zero/core/logx"
)

type CodeLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CodeLogic {
	return &CodeLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *CodeLogic) Code(req *types.Request) (resp *types.Response, err error) {
	TableUser:=make([]*Models.UserBasic,0)
	err = Models.Engine.Find(&TableUser)//这里查出来的是地址
	if err!=nil {
		log.Println("Find Error:",err)
	}
	//将以json格式输出
	bytes, err:= json.Marshal(TableUser)//转换成BYTE数组
	if err!=nil {
		log.Println("json Error:",err)
	}
	dst := new(bytes2.Buffer)
	err = json.Indent(dst, bytes, "", "  ") //转换为buffer
	if err!=nil {
		log.Println("Indent Error:",err)
	}
	println(dst.String())
	resp=new(types.Response)
	resp.Message=dst.String()
	return
}

一、用户模块

(1)密码登录

在 code.api 中写登录handler的相关配置

service code-api {
	@handler User//这个handler指的是生成在handler与logic下的文件所处理的句柄
	get /user/login (LoginRequest) returns (LoginReply)
}

type LoginRequest {
	Name     string `form:"name"`
	Password string `json:"password"`
}

type LoginReply {
	Token string `json:"token"`
}

 更新api,之后在 handler 与 logic 文件下都会出现新的.go文件

goctl api go  -api code.api -dir . -style go_zero

 在用户成功登陆之后,那么存到数据库的密码,我们也需要加密一下,这里用到的是MD5。

两个方法:

(1)md5.New() 初始化一个MD5对象,返回的是hash.Hash对象。

         函数原型: func New() hash.Hash

         该对象实现了hash.Hash的Sum接口。

(2)md5.Sum() 计算出MD5校验和。

         函数原型:func Sum(data []byte) [Size]byte
         他并不是对data进行校验计算,而是对hash.Hash对象内部存储的内容进行校验和计算然后将其追加到data的后面形成一个新的byte切片。该方法返回一个Size大小为16的byte数组,对于MD5来说就是一个128bit的16字节byte数组。

新建文件 helper,创建 helper.go 写入MD5函数 

package helper

import (
	"crypto/md5"
	"fmt"
)

func Md5(s string) string {
	return fmt.Sprintf("%x",md5.Sum([]byte(s)))
}

我们需要生成 token,而 token 需要用到 jwt 进行加密。这里介绍一下 jwt-go 库

使用 jwt-go 库生成 token,我们需要定义需求(claims),也就是说我们需要通过 jwt 传输的数据。假如我们需要传输 ID 和 Username,我们可以定义 Claims 结构体,其中包含 ID 和 Username 字段,还有在 jwt-go 包预定义的 jwt.StandardClaims。

使用 jwt-go 库根据指定的算法生成 jwt token ,主要用到两个方法:

(1)jwt.NewWithClaims 方法:

func jwt.NewWithClaims(method jwt.SigningMethod, claims jwt.Claims) *jwt.Token

jwt.NewWithClaims 方法根据 Claims 结构体创建 Token 示例。

参数 1 是 jwt.SigningMethod,最常用的是 jwt.SigningMethodHS256 这种 crypto.Hash 加密算法的方案。

参数 2 是 Claims,也就是我们自己定义的UserClaim,UserClaim嵌入在自定义类型中,以方便对标准声明进行编码,解析和验证。

(2)SignedString 方法:

func (*jwt.Token).SignedString(key interface{}) (string, error)

SignedString 方法根据传入的空接口类型参数 key,返回完整的签名令牌。

解析 token

使用 jwt-go 库解析 token

(1)jwt.ParseWithClaims 方法:

func jwt.ParseWithClaims(tokenString string, claims jwt.Claims, keyFunc jwt.Keyfunc) (*jwt.Token, error)

jwt.ParseWithClaims 方法用于解析鉴权的声明,返回 *jwt.Token。

创建文件夹define,用来定义token需要用到的claims

package define

import "github.com/dgrijalva/jwt-go"

type UserClaim struct {
	Id int
	Identity string
	Name string
	jwt.StandardClaims
}
var JwtKey="cloud-disk-key"

接下来定义生成token的方法,写在helper文件里

func GenerateToken(id int,name,identity string) (string,error) {
	uc:=define.UserClaim{
		Id:id,
		Identity: identity,
		Name: name,
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc) //生成token
	//给token进行加密
	tokenString, err := token.SignedString([]byte(define.JwtKey))
	if err!=nil {
		return "", err
	}
	return tokenString,nil
}

 准备工作都做好了,接下来我们来写user-login业务逻辑,user_logic.go 代码如下:

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogic {
	return &UserLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserLogic) User(req *types.LoginRequest) (resp *types.LoginReply, err error) {
	// todo: add your logic here and delete this line
	user := new(Models.UserBasic)
	//1.从数据库中查询当前用户
	get, err := Models.Engine.Where("name= ? AND password= ?", req.Name, helper.Md5(req.Password)).Get(user)
	if err!=nil {
		return nil, err
	}
	if !get {
		return nil,errors.New("用户名或密码错误")
	}
	//2.生成token
	token, err := helper.GenerateToken(user.ID, user.Name, user.Identity)
	if err!=nil {
		return nil,err
	}
	resp=new(types.LoginReply)
	resp.Token=token
	return
}

之后我们进行重新编译(下面两句话会频繁的用到,每次写完code.api都要goctl一下)

goctl api go -api code.api -dir . -style go_zero

 然后运行

go run code.go -f etc/code-api.yaml

使用postman软件,帮助测试 (这里在code.api中我将输入json改为了form形式)

基于Go语言的网盘开发(GloudDisk)_第8张图片

(2)邮箱注册

go get github.com/jordan-wright/email

 安装email,源址https://github.com/jordan-wright/email

go get github.com/go-redis/redis/v8

 安装redis,源址GitHub - go-redis/redis: Type-safe Redis client for Golang

$ go get github.com/satori/go.uuid

 安装uuid ,源址GitHub - satori/go.uuid: UUID package for Go

在 init.go 中加入 redis 的配置

package Models

import (
	"context"
	"github.com/go-redis/redis/v8"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"xorm.io/xorm"
)
var Engine=Init()
var ctx = context.Background()
var Redis = InitRedis()
func Init() *xorm.Engine {
	engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
	if err!=nil {
		log.Println("engine Error:",err)//log包的println会自动return
	}
	return engine
}
func InitRedis() *redis.Client {
	return redis.NewClient(&redis.Options{
		Addr:     "127.0.0.1:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})
}

 在 helper 中定义随机数生成,为了生成随机的验证码;以及 uuid ,为了生成 identity

package helper

import (
	"CloudDisk/code/define"
	"crypto/md5"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/jordan-wright/email"
	uuid "github.com/satori/go.uuid"
	"math/rand"
	"net/smtp"
	"time"
)

func Md5(s string) string {
	return fmt.Sprintf("%x",md5.Sum([]byte(s)))
}
func GenerateToken(id int,name,identity string) (string,error) {
	uc:=define.UserClaim{
		Id:id,
		Identity: identity,
		Name: name,
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc) //生成token
	//给token进行加密
	tokenString, err := token.SignedString([]byte(define.JwtKey))
	if err!=nil {
		return "", err
	}
	return tokenString,nil
}
func MailSendCode(mail,code string) error {
	e := email.NewEmail()
	e.From = ""//发送者姓名,发送者邮箱地址
	e.To = []string{mail}//接收着
	e.Subject = "验证码测试"//发送的主题
	e.HTML = []byte("

"+code+"

") err := e.Send("smtp.qq.com:25", smtp.PlainAuth("", "@qq.com", "jjj", "smtp.qq.com"), ) if err!=nil { return err } return nil } func RandCode() string { s:="1234567890" rand.Seed(time.Now().UnixNano()) code:="" for i := 0; i < 6; i++ { code+=string(s[rand.Intn(len(s))]) } return code } func GetUUID() string { return uuid.NewV4().String() }

 发送验证码的业务逻辑

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"
	"context"
	"errors"
	"time"

	"github.com/zeromicro/go-zero/core/logx"
)

type MailCodeSendLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewMailCodeSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MailCodeSendLogic {
	return &MailCodeSendLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *MailCodeSendLogic) MailCodeSend(req *types.MailCodeSendRequest) (resp *types.MailCodeSendReply, err error) {
	// todo: add your logic here and delete this line
	//该邮箱未被注册
	count, err := Models.Engine.Where("email= ?", req.Email).Count(new(Models.UserBasic))
	if err!=nil {
		return
	}
	if count>0 {
		err=errors.New("该邮箱已经被注册过了")
		return
	}
	//获取验证码
	code:=helper.RandCode()
	//存储验证码
	Models.Redis.Set(l.ctx, req.Email,code,time.Second * 300)
	//发送验证码
	err = helper.MailSendCode(req.Email, code)
	if err!=nil {
		return nil, err
	}
	return
}

用户注册的业务逻辑

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"
	"context"
	"errors"
	"github.com/zeromicro/go-zero/core/logx"
	"log"
)

type UserRegisterLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRegisterLogic {
	return &UserRegisterLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *types.UserRegisterReply, err error) {
	//判断code是否一致
	result, err := Models.Redis.Get(l.ctx, req.Email).Result()
	if err!=nil {
		return nil, errors.New("未获取该邮箱的验证码")
	}
	if result!=req.Code {
		err=errors.New("验证码错误")
		return
	}
	//判断用户名是否已经存在
	count, err := Models.Engine.Where("name= ?", req.Name).Count(new(Models.UserBasic))
	if err!=nil {
		return nil, err
	}
	if count>0 {
		err=errors.New("用户名已经存在")
		return
	}
	//数据入库,开始注册信息
	user:=&Models.UserBasic{
		Name: req.Name,
		Identity:helper.GetUUID(),
		Email: req.Email,
		Password: helper.Md5(req.Password),
	}
	insert, err := Models.Engine.Insert(user)
	if err!=nil {
		return nil, err
	}
	log.Println("insert user row:",insert)
	return
}

 (3)个人资料详情

 通过 identity 来获取用户信息

code.api新增:

service code-api {
	//用户登录
	@handler UserLogin//这个handler指的是生成在handler与logic下的文件所处理的句柄
	post /user/login (LoginRequest) returns (LoginReply)
	//用户详情
	@handler UserDetail
	post /user/detail (DetailRequest) returns (DetailReply)
}

type LoginRequest {
	Name     string `form:"name"`
	Password string `form:"password"`
}

type LoginReply {
	Token string `json:"token"`
}
type DetailRequest {
	Identity string `json:"identity"`
}
type DetailReply {
	Name  string `json:"name"`
	Email string `json:"email"`
}

 之后 goctl api go -api code.api -dir . -style go_zero 生成编译,编写detail业务逻辑

package logic

import (
	"CloudDisk/code/Models"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserDetailLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserDetailLogic {
	return &UserDetailLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserDetailLogic) UserDetail(req *types.DetailRequest) (resp *types.DetailReply, err error) {
	// todo: add your logic here and delete this line
	resp=&types.DetailReply{}
	m := new(Models.UserBasic)
	get, err := Models.Engine.Where("identity= ? ", req.Identity).Get(m)
	if err!=nil {
		return nil, err
	}
	if !get {
		return nil, errors.New("该用户不存在")
	}
	resp.Name=m.Name
	resp.Email=m.Email
	return
}

二、存储池模块

我们先修改一下数据库配置路由,在etc文件的 code_api.yaml 中加入如下:

Name: code-api
Host: 0.0.0.0
Port: 8888

Mysql:
  DataSource: root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8

Redis:
  Addr: 127.0.0.1:6379

在config文件中加入:

package config

import "github.com/zeromicro/go-zero/rest"

type Config struct {
	rest.RestConf
	Mysql struct{
		DataSource string
	}
	Redis struct{
		Addr string
	}
}

同样在 service_context.go 中也要加入:

package svc

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/internal/config"
	"CloudDisk/code/internal/middleware"
	"github.com/go-redis/redis/v8"
	"github.com/zeromicro/go-zero/rest"
	"xorm.io/xorm"
)

type ServiceContext struct {
	Config config.Config
	Engine *xorm.Engine
	RDB *redis.Client
	Auth rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,
		Engine:Models.Init(c.Mysql.DataSource),
		RDB:Models.InitRedis(c),
		Auth: middleware.NewAuthMiddleware().Handle,
	}
}

当然,在做以下业务逻辑的时候,首先需要加一个中间件,用来验证用户是否已经登录,如果不登录是无法使用以下功能的。在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,而全局中间件的服务范围则是整个服务。我们这里使用的是路由中间件 。对于中间件,需要添加 Middleware 声明。

所以我们需要再写一对 service code-api{},在这之上需要 Middleware 声明。例如文件上传的code.api配置,然后 goctl api go -api code.api -dir . -style go_zero 一下会自动生成Middleware的文件,在文件中配置如下

package middleware

import (
	"CloudDisk/code/helper"
	"net/http"
)

type AuthMiddleware struct {
}

func NewAuthMiddleware() *AuthMiddleware {
	return &AuthMiddleware{}
}

func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// TODO generate middleware implement function, delete after code implementation
		auth:=r.Header.Get("Authorization")
		if auth =="" {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Unauthorized"))
			return
		}
		uc, err := helper.AnalyzeToken(auth)
		if err!=nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error()))
			return
		}
		r.Header.Set("UserId",string(rune(uc.Id)))
		r.Header.Set("UserIdentity",uc.Identity)
		r.Header.Set("UserName",uc.Name)
		// Passthrough to next handler if need
	    next(w, r)
	}
}

这里用到了token的解析,具体如下:

//AnalyzeToken Token解析
func AnalyzeToken(token string) (*define.UserClaim, error) {
	uc:=new(define.UserClaim)
	claims, err := jwt.ParseWithClaims(token, uc, func(token *jwt.Token) (interface{}, error) {
		return []byte(define.JwtKey), nil
	})
	if err != nil {
		return nil,err
	}
	if !claims .Valid{
		return uc,errors.New("token is invalid")
	}
	return uc,err
}

  当然同样也要在 service_context.go 中也要加入Middleware 的声明。(代码跟上面一样,不在复制!)

(1)中心存储池资源管理

①文件上传

在Models里加入 RepositoryPool

package Models

import "time"

type RepositoryPool struct {
	Id        int
	Identity  string
	Hash      string
	Name      string
	Ext       string
	Size      int64
	Path      string
	createdAt time.Time `xorm:"created"`
	UpdatedAt time.Time `xorm:"updated"`
	DeletedAt time.Time `xorm:"deleted"`
}
func (table RepositoryPool) TableName() string {
	return "repository_pool"
}

 在code.api代码中,添加如下代码:

@server(
	middleware : Auth
)
service code-api {
	//文件上传
	@handler FileUpload
	post /file/upload (FileUploadRequest) returns (FileUploadReply)
}
type FileUploadRequest {
	Hash string `json:"hash,optional"`
	Name string `json:"name,optional"`
	Ext  string `json:"ext,optional"`
	Size int    `json:"size,optional"`
	Path string `json:"path,optional"`
}
type FileUploadReply {
	Identity string `json:"identity"`
	Ext      string `json:"ext"`
	Name     string `json:"name"`
}

 在文件上传这里我们采用 CosUpload 文件上传到腾讯云,帮助文档在这里---------------------------------------------------------------》》》》》对象存储 快速入门-SDK 文档-文档中心-腾讯云

在 helper 文件中加入 CosUpload 文件上传的业务逻辑

//CosUpload 文件上传到腾讯云
func CosUpload(r *http.Request) (string,error) {
	u, _ := url.Parse(define.CosBucket)
	b := &cos.BaseURL{BucketURL: u}
	c := cos.NewClient(b, &http.Client{
		Transport: &cos.AuthorizationTransport{
			//如实填写账号和密钥,也可以设置为环境变量
			SecretID:  define.TencentSecretID,
			SecretKey: define.TencentSecretKey,
		},
	})
	file, fileHeader, err := r.FormFile("file")
	name := "cloud-disk/"+GetUUID()+path.Ext(fileHeader.Filename)
	_, err = c.Object.Put(context.Background(), name, file,nil)
	if err != nil {
		panic(err)
	}
	return define.CosBucket+"/"+name,nil
}

file_upload_handler.go 中写入如下代码:这次我们是先判断一下文件是否上传过,之后将信息写给 logic 中的 req 。

package handler

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"crypto/md5"
	"fmt"
	"net/http"
	"path"

	"CloudDisk/code/internal/logic"
	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"
	"github.com/zeromicro/go-zero/rest/httpx"
)

func FileUploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req types.FileUploadRequest
		if err := httpx.Parse(r, &req); err != nil {
			httpx.Error(w, err)
			return
		}
		file, fileHeader, err2 := r.FormFile("file")
		if err2!=nil {
			return
		}
		//判断文件是否存在
		bytes := make([]byte, fileHeader.Size)
		_, err2 = file.Read(bytes)
		if err2!=nil {
			return
		}
		hash:=fmt.Sprintf("%x",md5.Sum(bytes))
		rp:=new(Models.RepositoryPool)
		get, err2 := svcCtx.Engine.Where("hash=?", hash).Get(rp)
		if err2!=nil {
			return
		}
		if get {
			httpx.OkJson(w,&types.FileUploadReply{Identity: rp.Identity, Ext: rp.Ext, Name: rp.Name})
			return
		}
		//往cos中存储文件
		cosPath, err2 := helper.CosUpload(r)
		if err2!=nil {
			return
		}
		//往 logic 中传递req
		req.Name=fileHeader.Filename
		req.Ext=path.Ext(fileHeader.Filename)
		req.Size= int(fileHeader.Size)
		req.Hash=hash
		req.Path=cosPath

		l := logic.NewFileUploadLogic(r.Context(), svcCtx)
		resp, err := l.FileUpload(&req)
		if err != nil {
			httpx.Error(w, err)
		} else {
			httpx.OkJson(w, resp)
		}
	}
}

file_upload_logic.go 业务逻辑代码如下:

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"
	"fmt"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type FileUploadLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewFileUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FileUploadLogic {
	return &FileUploadLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *FileUploadLogic) FileUpload(req *types.FileUploadRequest) (resp *types.FileUploadReply, err error) {
	// todo: add your logic here and delete this line
	rp := &Models.RepositoryPool{
		Identity: helper.GetUUID(),
		Hash:     req.Hash,
		Name:     req.Name,
		Ext:      req.Ext,
		Size:     int64(req.Size),
		Path:     req.Path,
	}
	_, err = l.svcCtx.Engine.Insert(rp)
	if err!=nil {
		return nil, err
	}
	resp=new(types.FileUploadReply)
	resp.Identity=rp.Identity
	resp.Ext=rp.Ext
	resp.Name=rp.Name
	fmt.Println(resp.Identity)
	return
}

(2)个人存储池资源管理 

①文件关联存储

code.api

//用户文件的关联存储
	@handler UserRepositorySave
	post /user/repository/save (UserRepositorySaveRequest) returns (UserRepositorySaveReply)
type UserRepositorySaveRequest {
	ParentId           int64  `json:"parentId"`
	RepositoryIdentity string `json:"repositoryIdentity"`
	Ext                string `json:"ext"`
	Name               string `json:"name"`
}
type UserRepositorySaveReply {
}

 Models新增

package Models

import "time"

type UserRepository struct {
	Id                 int
	Identity           string
	UserIdentity       string
	ParentId           int64
	RepositoryIdentity string
	Ext                string
	Name               string
	CreatedAt          time.Time `xorm:"created"`
	UpdatedAt          time.Time `xorm:"updated"`
	DeletedAt          time.Time `xorm:"deleted"`
}

func (table UserRepository) TableName() string {
	return "user_repository"
}

 在 user_repository_save_handler.go 修改一下 UserRepositorySave 函数的参数,将UserIdentity 传入,代码如下:

package handler

import (
	"net/http"

	"CloudDisk/code/internal/logic"
	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"
	"github.com/zeromicro/go-zero/rest/httpx"
)

func UserRepositorySaveHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req types.UserRepositorySaveRequest
		if err := httpx.Parse(r, &req); err != nil {
			httpx.Error(w, err)
			return
		}

		l := logic.NewUserRepositorySaveLogic(r.Context(), svcCtx)
		resp, err := l.UserRepositorySave(&req,r.Header.Get("UserIdentity"))
		if err != nil {
			httpx.Error(w, err)
		} else {
			httpx.OkJson(w, resp)
		}
	}
}

 接下来的每一个业务逻辑块都需要在 handler 里加入,之后我就不再详细说明了。

logic业务逻辑:(非常简单,就是个插入) 

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserRepositorySaveLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserRepositorySaveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRepositorySaveLogic {
	return &UserRepositorySaveLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserRepositorySaveLogic) UserRepositorySave(req *types.UserRepositorySaveRequest, userIdentity string) (resp *types.UserRepositorySaveReply, err error) {
	// todo: add your logic here and delete this line
	ur := &Models.UserRepository{
		Identity:           helper.GetUUID(),
		UserIdentity:       userIdentity,
		ParentId:           req.ParentId,
		RepositoryIdentity: req.RepositoryIdentity,
		Ext:                req.Ext,
		Name:               req.Name,
	}
	_, err = l.svcCtx.Engine.Insert(ur)
	if err!=nil {
		return
	}
	return
}

②用户文件列表 

code.api 

	//用户文件列表
	@handler UserFileList
	get /user/file/list (UserFileListReguest) returns (UserFileListReply)
type UserFileListReguest {
	Id   int64 `json:"id,optional"`
	Page int   `json:"page,optional"`
	Size int   `json:"size,optional"`
}

type UserFileListReply {
	List  []*UserFile `json:"list"`
	Count int         `json:"count"`
}

logic 业务逻辑做到了分页的多表 join 查询 

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/define"
	"context"
	"time"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserFileListLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserFileListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileListLogic {
	return &UserFileListLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserFileListLogic) UserFileList(req *types.UserFileListReguest,userIdentity string) (resp *types.UserFileListReply, err error) {
	// todo: add your logic here and delete this line
	uf := make( []*types.UserFile,0)
	//var cnt int64
	resp = new(types.UserFileListReply)
	size:=req.Size
	if size==0 {
		size=define.PageSize
	}
	page:=req.Page
	if page ==0{
		page=1
	}
	offset:=(page-1)*size
	//查询用户文件列表
	l.svcCtx.Engine.ShowSQL(true)//这样就可以看到运行时的sql语句
	err = l.svcCtx.Engine.Table("user_repository"). Where("parent_id = ? AND user_identity = ? ",req.Id,userIdentity).
		Select("user_repository.id,user_repository.identity,user_repository.repository_identity,user_repository.ext,"+
			"user_repository.name,repository_pool.path,repository_pool.size").
		Join("LEFT","repository_pool","user_repository.repository_identity=repository_pool.identity").
		Where("user_repository.deleted_at=? OR user_repository.deleted_at IS NULL",time.Time{}.Format("2006-01-02 15:04:05")).
		Limit(size,offset).Find(&uf)
	if err != nil {
		return
	}
	count, err := l.svcCtx.Engine.Where("parent_id = ? AND user_identity = ? ", req.Id, userIdentity).Count(new(Models.UserRepository))
	if err!=nil {
		return
	}
	resp.List = uf
	resp.Count = int(count)

	return
}

③用户文件名称修改

code.api

	//用户文件名称修改
	@handler UserFileNameUpdate
	post /user/file/name/update (UserFileNameUpdateReguest) returns (UserFileNameUpdateReply)
type UserFileNameUpdateReguest {
	Identity string `json:"identity"`
	Name     string `json:"name"`
}

type UserFileNameUpdateReply {
}

 logic 业务实现

package logic

import (
	"CloudDisk/code/Models"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserFileNameUpdateLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserFileNameUpdateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileNameUpdateLogic {
	return &UserFileNameUpdateLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserFileNameUpdateLogic) UserFileNameUpdate(req *types.UserFileNameUpdateReguest,userIdentity string) (resp *types.UserFileNameUpdateReply, err error) {
	// todo: add your logic here and delete this line
	//判断当前名称在该层下面是否存在
	count, err := l.svcCtx.Engine.Where("name=? AND parent_id=(SELECT parent_id FROM user_repository ur WHERE ur.identity=?)", req.Name, req.Identity).Count(new(Models.UserRepository))
	if err!=nil {
		return nil, err
	}
	if count >0{
		return nil,errors.New("该名称已经存在")
	}
	//文件修改
	data := &Models.UserRepository{Name: req.Name}
	l.svcCtx.Engine.Where("identity = ? AND user_identity = ? ", req.Identity, userIdentity).Update(data)
	if err != nil {
		return
	}
	return
}

④用户文件夹创建

code.api

	//用户文件夹创建
	@handler UserFolderCreate
	post /user/folder/create (UserFolderCreateReguest) returns (UserFolderCreateReply)
type UserFolderCreateReguest {
	Name     string `json:"name"`
	ParentId int64  `json:"parent_id"`
}

type UserFolderCreateReply {
	Identity string `json:"identity"`
}

 logic

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserFolderCreateLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserFolderCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFolderCreateLogic {
	return &UserFolderCreateLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserFolderCreateLogic) UserFolderCreate(req *types.UserFolderCreateReguest,userIdentity string) (resp *types.UserFolderCreateReply, err error) {
	// todo: add your logic here and delete this line
	//判断当前名称在该层下面是否存在
	count, err := l.svcCtx.Engine.Where("name=? AND parent_id=?", req.Name, req.ParentId).Count(new(Models.UserRepository))
	if err!=nil {
		return nil, err
	}
	if count >0{
		return nil,errors.New("该名称已经存在")
	}
	//创建文件夹
	data:=&Models.UserRepository{
		Identity: helper.GetUUID(),
		UserIdentity: userIdentity,
		ParentId: req.ParentId,
		Name: req.Name,
	}
	_, err = l.svcCtx.Engine.Insert(data)
	if err != nil {
		return
	}
	return
}

⑤用户文件删除

code.api

	//用户文件删除
	@handler UserFileDelete
	post /user/file/delete (UserFileDeleteReguest) returns (UserFileDeleteReply)
type UserFileDeleteReguest {
	Identity string `json:"identity"`
}

type UserFileDeleteReply {
}

 logic

package logic

import (
	"CloudDisk/code/Models"
	"context"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserFileDeleteLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserFileDeleteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileDeleteLogic {
	return &UserFileDeleteLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserFileDeleteLogic) UserFileDelete(req *types.UserFileDeleteReguest,userIdentity string) (resp *types.UserFileDeleteReply, err error) {
	// todo: add your logic here and delete this line
	_, err = l.svcCtx.Engine.Where("user_identity=? AND identity=?", userIdentity, req.Identity).Delete(new(Models.UserRepository))
	if err!=nil {
		return
	}
	return
}

⑥用户文件移动

code.api,注意是 PUT 请求

	//用户文件移动
	@handler UserFileMove
	put  /user/file/move (UserFileMoveReguest) returns (UserFileMoveReply)
type UserFileMoveReguest {
	Identity       string `json:"identity"`
	ParentIdentity string `json:"parentIdentity"`
}

type UserFileMoveReply {
}

 logic

package logic

import (
	"CloudDisk/code/Models"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type UserFileMoveLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewUserFileMoveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileMoveLogic {
	return &UserFileMoveLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *UserFileMoveLogic) UserFileMove(req *types.UserFileMoveReguest,userIdentity string) (resp *types.UserFileMoveReply, err error) {
	// todo: add your logic here and delete this line
	parentDate := new(Models.UserRepository)
	has, err := l.svcCtx.Engine.Where("identity=? AND user_identity=?", req.ParentIdentity, userIdentity).Get(parentDate)
	if err!=nil {
		return nil, err
	}
	if !has {
		return nil,errors.New("文件夹不存在!")
	}
	//更新记录的ParentID
	_, err = l.svcCtx.Engine.Where("identity=?", req.Identity).Update(Models.UserRepository{
		ParentId: int64(parentDate.Id),
	})
	return
}

三、文件共享模块

(1)文件分享

①创建分享记录

code.api

	//创建分享记录
	@handler ShareBasicCreate
	post /share/basic/create (ShareBasicCreateRequest) returns (ShareBasicCreateReply)
type ShareBasicCreateRequest {
	RepositoryIdentity string `json:"repository_identity"`
	ExpiredTime        int    `json:"expired_time"`
}

type ShareBasicCreateReply {
	Identity string `json:"identity"`
}

logic

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type ShareBasicCreateLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewShareBasicCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicCreateLogic {
	return &ShareBasicCreateLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ShareBasicCreateLogic) ShareBasicCreate(req *types.ShareBasicCreateRequest, userIdentity string) (resp *types.ShareBasicCreateReply, err error) {
	// todo: add your logic here and delete this line
	uuid := helper.GetUUID()
	data := &Models.ShareBasic{
		Identity:           uuid,
		UserIdentity:       userIdentity,
		RepositoryIdentity: req.RepositoryIdentity,
		ExpiredTime:        req.ExpiredTime,
	}
	_, err = l.svcCtx.Engine.Insert(data)
	if err != nil {
		return
	}
	resp = &types.ShareBasicCreateReply{
		Identity: uuid,
	}
	return
}

②获取资源详情

对于这个模块,就不用登录的中间件了,因为未登录的应该也可以获取到别人分享的文件。所以就写在用户登录等的那个api块里就行。

code.api

	//获取资源详情
	@handler ShareBasicDetail
	get /share/basic/detail (ShareBasicDetailRequest) returns (ShareBasicDetailReply)
type ShareBasicDetailRequest {
	Identity string `json:"identity"`
}

type ShareBasicDetailReply {
	RepositoryIdentity string `json:"repository_identity"`
	Name               string `json:"name"`
	Ext                string `json:"ext"`
	Size               int64  `json:"size"`
	Path               string `json:"path"`
}

Models

package Models

import "time"

type ShareBasic struct {
	Id int
	Identity string
	UserIdentity string
	RepositoryIdentity string
	ExpiredTime int
	ClickNum int
	CreatedAt time.Time `xorm:"created"`
	UpdatedAt time.Time `xorm:"updated"`
	DeletedAt time.Time `xorm:"deleted"`
}

func (table ShareBasic)TableName( ) string {
	return "share_basic"
}

logic

package logic

import (
	"context"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type ShareBasicDetailLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewShareBasicDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicDetailLogic {
	return &ShareBasicDetailLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ShareBasicDetailLogic) ShareBasicDetail(req *types.ShareBasicDetailRequest) (resp *types.ShareBasicDetailReply, err error) {
	// todo: add your logic here and delete this line
	//对分享记录的点击次数进行 +1 操作
	_, err = l.svcCtx.Engine.Exec("UPDATE share_basic SET click_num = click_num + 1 WHERE identity = ?", req.Identity)
	if err!=nil {
		return
	}
	//获取资源的详细信息
	resp= new(types.ShareBasicDetailReply)
	_, err = l.svcCtx.Engine.Table("share_basic").
		Select("share_basic.repository_identity,repository_pool.name,repository_pool.ext,repository_pool.size,repository_pool.path").
		Join("LEFT", "repository_pool", "share_basic.repository_identity=repository_pool.identity").
		Where("share_basic.identity=?", req.Identity).Get(resp)
	return
}

③资源保存

需要用户登录的中间件,code.api 

	//资源保存
	@handler ShareBasicSave
	post /share/basic/save (ShareBasicSaveRequest) returns (ShareBasicSaveReply)
type ShareBasicSaveRequest {
	RepositoryIdentity string `json:"repository_identity"`
	ParentId           int64  `json:"parent_id"`
}

type ShareBasicSaveReply {
	Identity string `json:"identity"`
}

logic

package logic

import (
	"CloudDisk/code/Models"
	"CloudDisk/code/helper"
	"context"
	"errors"

	"CloudDisk/code/internal/svc"
	"CloudDisk/code/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type ShareBasicSaveLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewShareBasicSaveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicSaveLogic {
	return &ShareBasicSaveLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ShareBasicSaveLogic) ShareBasicSave(req *types.ShareBasicSaveRequest,userIdentity string) (resp *types.ShareBasicSaveReply, err error) {
	// todo: add your logic here and delete this line
	//获取资源详情
	rp:=new(Models.RepositoryPool)
	has, err := l.svcCtx.Engine.Where("identity=?", req.RepositoryIdentity).Get(rp)
	if err!=nil {
		return nil, err
	}
	if !has {
		return nil,errors.New("资源不存在")
	}
	//user_repository 资源保存
	ur:=&Models.UserRepository{
		Identity: helper.GetUUID(),
		UserIdentity: userIdentity,
		ParentId: req.ParentId,
		RepositoryIdentity: req.RepositoryIdentity,
		Ext: rp.Ext,
		Name: rp.Name,
	}
	_, err = l.svcCtx.Engine.Insert(ur)
	resp=new(types.ShareBasicSaveReply)
	resp.Identity=ur.Identity
	return
}

你可能感兴趣的:(go的学习,golang,开发语言,后端)