既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
"github.com/zeromicro/go-zero/core/logx"
)
type LoginUserLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewLoginUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginUserLogic {
return &LoginUserLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// LoginUser 用户登录
func (l *LoginUserLogic) LoginUser(in *ucenter.User) (*ucenter.UserLoginResp, error) {
// todo: add your logic here and delete this line
//return &ucenter.UserLoginResp{}, nil
return l.LoginSuccess(in)
}
func (l *LoginUserLogic) LoginSuccess(in *ucenter.User) (*ucenter.UserLoginResp, error) {
AccessSecret := l.svcCtx.Config.JWT.AccessSecret
AccessExpire := l.svcCtx.Config.JWT.AccessExpire
now := time.Now().Unix()
jwtToken, err := utils.GenerateJwtToken(AccessSecret, now, AccessExpire, in.Id)
if err != nil {
return nil, err
}
resp := &ucenter.UserLoginResp{}
copier.Copy(resp, in)
resp.AccessToken = jwtToken
resp.AccessExpire = now + AccessExpire
resp.RefreshAfter = now + AccessExpire/2
return resp, nil
}
---
**3. API服务调用RPC服务的登录接口**
API服务的`svc`下的`servicecontext.go`加入待调用服务接口
这里加入了 `ucentergorm.UcenterGorm` 和 `ucentersqlx.UcenterSqlx`,这里使用`ucentersqlx.UcenterSqlx`里的登录校验接口。
package svc
import (
“github.com/zeromicro/go-zero/rest”
“github.com/zeromicro/go-zero/zrpc”
“go-zero-micro/api/code/ucenterapi/internal/config”
“go-zero-micro/api/code/ucenterapi/internal/middleware”
“go-zero-micro/rpc/code/ucenter/client/ucentergorm”
“go-zero-micro/rpc/code/ucenter/client/ucentersqlx”
)
type ServiceContext struct {
Config config.Config
Check rest.Middleware
UcenterGormRpc ucentergorm.UcenterGorm //gorm方式的接口
UcenterSqlxRpc ucentersqlx.UcenterSqlx //sqlx方式的接口
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
Check: middleware.NewCheckMiddleware().Handle,
UcenterGormRpc: ucentergorm.NewUcenterGorm(zrpc.MustNewClient(c.UCenterRpc)),
UcenterSqlxRpc: ucentersqlx.NewUcenterSqlx(zrpc.MustNewClient(c.UCenterRpc)),
}
}
API服务的登录逻辑调用RPC服务的登录校验接口
package login
import (
“context”
“github.com/jinzhu/copier”
“go-zero-micro/common/errorx”
“go-zero-micro/rpc/code/ucenter/ucenter”
"go-zero-micro/api/code/ucenterapi/internal/svc"
"go-zero-micro/api/code/ucenterapi/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginByPasswordLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLoginByPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginByPasswordLogic {
return &LoginByPasswordLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginByPasswordLogic) LoginByPassword(req *types.UserLoginPasswordModel) (resp *types.UserLoginResp, err error) {
// todo: add your logic here and delete this line
param := &ucenter.User{}
copier.Copy(param, req)
loginRes, err := l.svcCtx.UcenterSqlxRpc.LoginUser(l.ctx, param)
if err != nil {
return nil, errorx.NewDefaultError(errorx.UserLoginPasswordErrorCode)
}
res := &types.UserLoginResp{}
copier.Copy(res, loginRes)
return res, nil
}
4. 登录测试
请求地址:<http://localhost:8888/login/loginByPassword>
请求方式:POST
请求格式:JSON
请求数据:`{"account":"hello","password":"123456"}`
返回结果:
{
“code”: 200,
“msg”: “success”,
“success”: true,
“data”: {
“id”: 0,
“account”: “hello”,
“username”: “”,
“gender”: 0,
“avatar”: “”,
“token”: “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODgzMDk2NDIsImlhdCI6MTY4ODIyMzI0MiwidXNlcklkIjowfQ.XYRNMXH0ViWai9N3THTe2X_38st_kFLb76TDHM8ko-k”,
“accessExpire”: 1688309642,
“refreshAfter”: 1688266442
}
}
5. 未登录测试
请求地址:<http://localhost:8888/ucenter/getUserById>
请求方式:GET
请求格式:JSON
请求数据:`?id=1`
返回结果:`401 Unauthorized`
6. 已登录测试
请求地址:<http://localhost:8888/ucenter/getUserById>
请求方式:GET
请求格式:URL
请求数据:`?id=1`
请求头:需要在`Authorization`中选择 `Bearer Token`,填入在第 4步获取的`token`。
返回结果:
{
“code”: 200,
“msg”: “success”,
“success”: true,
“data”: null
}
### 2.3 服务注册/发现
参考1:[服务连接]( )
参考2:[服务发现/直连模式]( )
go-zero的服务发现支持以下几种形式:
1. K8S服务发现
2. ETCD【默认】
3. IP直连
4. Consul
5. Nacos
6. PolarisMash
在 go-zero 中,支持 ETCD【默认】服务注册和直连模式。
这里仅以ETCD、IP直连。
**1 ETCD:**
`go-zero`默认是通过ETCD注册发现,在`2.2 API简单调用RPC服务`基础上,通过查看API服务的登录调用RPC服务登录校验的过程,可以发现底层还是通过`GRPC`调用,`go-zero`只是进一步进行了封装。
ETCD方式的流程和代码与`2.2 API简单调用RPC服务`中 `API服务调用RPC服务的登录接口`内容一致,这里不再重复。
**有以下两点需要注意:**
1. `go-zero`使用k8s时,服务发现不能使用直连方式,可能会导致负载不均衡。
2. `API`服务的`yaml`中`ListenOn: 0.0.0.0:8080`,和`RPC`服务的`yaml`中`Host: 0.0.0.0`,设置成`0.0.0.0`会自动获取内网IP,如果设置成固定内网ip的话就是直接写成指定的IP了。
---
**2 IP直连:(不推荐,RPC服务的具体地址不能动态修改)**
**有两种方式**:
方式一(不推荐):使用grpc,因为改动位置过多,改动较大。
[本次示例代码]( )
参考2:[服务发现/直连模式]( )
直连分为两种模式,一种是直连单个服务,一种是直连服务集群。
1. RPC服务
使用直连模式时`RPC`服务的`yaml`配置中仅需要去除 `ETCD` 配置即可,`go-zero` 自动识别,最简配置参考:
`只需要去除在ETCD注册服务的配置即可`。
Name: ucenter.rpc
ListenOn: 0.0.0.0:8080
#Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
JWT:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
修改完后重启RPC服务即可。
2. API服务
package svc
import (
“github.com/zeromicro/go-zero/rest”
“github.com/zeromicro/go-zero/zrpc”
“go-zero-micro/api/code/ucenterapi/internal/config”
“go-zero-micro/api/code/ucenterapi/internal/middleware”
“go-zero-micro/rpc/code/ucenter/ucenter”
)
type ServiceContext struct {
Config config.Config
Check rest.Middleware
//UcenterGormRpc ucentergorm.UcenterGorm //gorm方式的接口
//UcenterSqlxRpc ucentersqlx.UcenterSqlx //sqlx方式的接口
UcenterSqlxDC ucenter.UcenterSqlxClient //sqlx方式的GRPC接口
}
func NewServiceContext(c config.Config) *ServiceContext {
conn := zrpc.MustNewClient(zrpc.RpcClientConf{
Target: “dns:///127.0.0.1:8080”,
})
return &ServiceContext{
Config: c,
Check: middleware.NewCheckMiddleware().Handle,
//UcenterGormRpc: ucentergorm.NewUcenterGorm(zrpc.MustNewClient(c.UCenterRpc)),
//UcenterSqlxRpc: ucentersqlx.NewUcenterSqlx(zrpc.MustNewClient(c.UCenterRpc)),
UcenterSqlxDC: ucenter.NewUcenterSqlxClient(conn.Conn()),
}
}
登录接口:
package login
import (
“context”
“github.com/jinzhu/copier”
“go-zero-micro/common/errorx”
“go-zero-micro/rpc/code/ucenter/ucenter”
"go-zero-micro/api/code/ucenterapi/internal/svc"
"go-zero-micro/api/code/ucenterapi/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginByPasswordLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLoginByPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginByPasswordLogic {
return &LoginByPasswordLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginByPasswordLogic) LoginByPassword(req *types.UserLoginPasswordModel) (resp *types.UserLoginResp, err error) {
// todo: add your logic here and delete this line
param := &ucenter.User{}
copier.Copy(param, req)
//loginRes, err := l.svcCtx.UcenterSqlxRpc.LoginUser(l.ctx, param)
loginRes, err := l.svcCtx.UcenterSqlxDC.LoginUser(l.ctx, param)
if err != nil {
return nil, errorx.NewDefaultError(errorx.UserLoginPasswordErrorCode)
}
res := &types.UserLoginResp{}
copier.Copy(res, loginRes)
return res, nil
}
---
方式2(推荐):
[本次示例代码]( )
参考:[二十二、api与rpc直连方式服务发现]( ),只需要更改RPC和API的yaml文件即可。
1. RPC服务
使用直连模式时`RPC`服务的`yaml`配置中仅需要去除 `ETCD` 配置即可,`go-zero` 自动识别,最简配置参考:
`只需要去除在ETCD注册服务的配置即可`。
Name: ucenter.rpc
ListenOn: 0.0.0.0:8080
#Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
JWT:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
修改完后重启RPC服务即可。
2. API服务
使用直连模式时`API`服务的`yaml`配置中仅需要将 `ETCD` 配置去除,同时在`Endpoints`中添加服务端ip地址即可,可添加多个,`go-zero` 自动识别:
Name: ucenter-api
Host: 0.0.0.0
Port: 8888
Auth:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
#web请求到此api服务的超时时间
Timeout: 10000
将请求体最大允许字节数从1MB改为1000MB
MaxBytes: 1048576000
#文件
UploadFile:
MaxFileNum: 1000
MaxFileSize: 1048576000 # 1000MB
SavePath: projects/go-zero-micro/uploadFiles/
UCenter 服务
UCenterRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
Endpoints:
- 127.0.0.1:8080
#api请求rpc服务的超时时间
Timeout: 10000
#日志配置
Log:
Mode: file
Path: log/go-zero-micro
Level: error
Compress: true
KeepDays: 180
---
### 2.4 文件上传/下载&分组
[本次示例代码]( )
这里的文件上传包括`api服务`与`rpc服务`之间文件传输,具体涉及到的文件以及变动,可查看本次示例代码。
先从rpc服务开始写代码,然后api服务。
**1 PRC服务代码**
1. `yaml`配置文件:设置文件的存储位置及文件大小的限制。
Name: ucenter.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
JWT:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
#文件
UploadFile:
MaxFileNum: 1000
MaxFileSize: 1048576000 # 1000MB
SavePath: projects/go-zero-micro/uploadFiles/
#日志配置
Log:
Mode: file
Path: log/go-zero-micro/ucenterpc
Level: error
Compress: true
KeepDays: 180
2. 配置映射类:
package config
import “github.com/zeromicro/go-zero/zrpc”
type Config struct {
zrpc.RpcServerConf
JWT struct {
AccessSecret string
AccessExpire int64
}
UploadFile UploadFile
}
type UploadFile struct {
MaxFileNum int64
MaxFileSize int64
SavePath string
}
3. `ucenter.proto`(这里只显示了部分代码,全部的请到`github`查看):文件下载是使用流的方式。
message FileInfo {
//文件-id
int64 FileId = 1;
//文件-名称
string FileName = 2;
//文件类型 1:图片 2:音频 3:视频 4:文件
string FileType = 3;
//文件大小
int64 FileSize = 4;
//文件流
bytes FileData = 5;
//文件地址
string FileUrl = 6;
}
// 文件集合
message FileList {
//父级-id
int64 PidId = 1;
//父级-id
int64 Type = 2;
//文件信息
repeated FileInfo file = 3;
// 创建者-id
int64 CreatedBy = 4;
// 创建时间
string CreatedAt = 5;
// 更新者-id
int64 UpdatedBy = 6;
// 更新时间
string UpdatedAt = 7;
// 删除者-id
int64 DeletedBy = 8;
// 删除时间
string DeletedAt = 9;
}
service fileStorage {
//文件上传
rpc FileUpload(FileList) returns(BaseResp);
//文件下载
rpc FileDownload(FileInfo) returns(stream FileInfo);
}
4. 根据`ucenter.proto`生成`go-zero`的`rpc`服务代码。
goctl rpc protoc ./proto/ucenter.proto --go_out=./code/ucenter --go-grpc_out=./code/ucenter --zrpc_out=./code/ucenter --multiple
5. 文件上传功能代码位置:`internal/logic/filestorage/fileuploadlogic.go`
核心代码逻辑:
// 文件上传
func (l *FileUploadLogic) FileUpload(in *ucenter.FileList) (*ucenter.BaseResp, error) {
// todo: add your logic here and delete this line
SavePath := l.svcCtx.Config.UploadFile.SavePath //上传文件的存储路径
utils.CreateDir(SavePath)
for _, fileInfo := range in.File {
//获取文件名称带后缀
fileNameWithSuffix := path.Base(fileInfo.FileName)
//获取文件的后缀(文件类型)
fileType := path.Ext(fileNameWithSuffix)
//生成UUID防止文件被覆盖
uuidName := strings.Replace(uuid.NewV4().String(), “-”, “”, -1)
saveName := uuidName + fileType
saveFullPath := SavePath + saveName
err := ioutil.WriteFile(saveFullPath, fileInfo.FileData, 0644)
if err != nil {
// handle error
}
}
return &ucenter.BaseResp{
Data: "upload success",
}, nil
}
6. 文件下载功能代码位置:`internal/logic/filestorage/filedownloadlogic.go`
核心代码逻辑:
// 文件下载
func (l *FileDownloadLogic) FileDownload(in *ucenter.FileInfo, stream ucenter.FileStorage_FileDownloadServer) error {
// todo: add your logic here and delete this line
SavePath := l.svcCtx.Config.UploadFile.SavePath //上传文件的存储路径
filePath := SavePath + in.FileUrl
\_, err := os.Stat(filePath)
if err != nil || os.IsNotExist(err) {
return errors.New("文件不存在")
}
bytes, err := os.ReadFile(filePath)
if err != nil {
return errors.New("读取文件失败")
}
response := &ucenter.FileInfo{}
copier.Copy(response, in)
response.FileName = "go-zero.png"
response.FileData = bytes
if err := stream.Send(response); err != nil {
return err
}
return nil
}
**2 API服务代码**
1. `yaml`配置文件:
* 修改`MaxBytes`参数是为了调整api服务处理请求体大小限制,如果`MaxBytes`不调大,上传文件会失败,同时时api服务会返回 `413Request Entity Too Large`。
* 但是如果文件过大,则有可能导致内存溢出,因此在`yaml`中修改`MaxBytes`参数时,`MaxBytes`又不能设置的过大。
Name: ucenter-api
Host: 0.0.0.0
Port: 8888
Auth:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
#web请求到此api服务的超时时间
Timeout: 10000
将请求体最大允许字节数从1MB改为1000MB
MaxBytes: 1048576000
#文件
UploadFile:
MaxFileNum: 1000
MaxFileSize: 1048576000 # 1000MB
SavePath: projects/go-zero-micro/uploadFiles/
UCenter 服务
UCenterRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
#api请求rpc服务的超时时间
Timeout: 10000
#日志配置
Log:
Mode: file
Path: log/go-zero-micro/ucenterapi
Level: error
Compress: true
KeepDays: 180
2. `file.api`:注意:这里的文件相关接口上没有加鉴权。
syntax = “v1”
info(
title : “go-zero-micro”
desc: “userapi”
author: “ximuqi”
email: “xxx”
version: “0.0.1”
)
type (
/* 1 上传文件 */
FileUploadReq {
Id int64 form:"id"
// 父级-id
Type int64 form:"type,optional"
// 类型 1:类型1;2:类型2
FileList []*byte form:"fileList,optional"
// 文件列表
}
/\* 2 下载/预览文件 \*/
FileShowReq {
Id int64 `form:"id"` // 文件-id
FileUrl string `form:"fileUrl,optional"` // 文件地址
}
)
@server(
group: fileStorage
prefix: /fileStorage
)
service ucenter-api {
@doc(
summary: “1 上传文件”
)
@handler fileUpload
post /fileUpload (FileUploadReq) returns (BaseModel)
@doc(
summary: "2 文件下载"
)
@handler fileDownload
get /fileDownload (FileShowReq)
@doc(
summary: "3 文件预览"
)
@handler filePreview
get /filePreview (FileShowReq)
}
3. 根据`file.api`生成go-zero的api服务代码。
goctl api go -api ./doc/all.api -dir ./code/ucenterapi
4. `internal/svc/servicecontext.go`加入rpc服务的文件存储相关服务接口
package svc
import (
“github.com/zeromicro/go-zero/rest”
“github.com/zeromicro/go-zero/zrpc”
“go-zero-micro/api/code/ucenterapi/internal/config”
“go-zero-micro/api/code/ucenterapi/internal/middleware”
“go-zero-micro/rpc/code/ucenter/client/filestorage”
“go-zero-micro/rpc/code/ucenter/client/ucentergorm”
“go-zero-micro/rpc/code/ucenter/client/ucentersqlx”
)
type ServiceContext struct {
Config config.Config
Check rest.Middleware
UcenterGormRpc ucentergorm.UcenterGorm //gorm方式的接口
UcenterSqlxRpc ucentersqlx.UcenterSqlx //sqlx方式的接口
FileStorageRpc filestorage.FileStorage //文件存储相关接口
}
func NewServiceContext(c config.Config) *ServiceContext {
uCenterRpcClient := zrpc.MustNewClient(c.UCenterRpc)
return &ServiceContext{
Config: c,
Check: middleware.NewCheckMiddleware().Handle,
UcenterGormRpc: ucentergorm.NewUcenterGorm(uCenterRpcClient),
UcenterSqlxRpc: ucentersqlx.NewUcenterSqlx(uCenterRpcClient),
FileStorageRpc: filestorage.NewFileStorage(uCenterRpcClient),
}
}
5. 文件上传功能代码位置:`internal/logic/fileStorage/fileuploadlogic.go`
package fileStorage
import (
“bytes”
“context”
“fmt”
“github.com/jinzhu/copier”
uuid “github.com/satori/go.uuid”
“go-zero-micro/common/utils”
“go-zero-micro/rpc/code/ucenter/ucenter”
“io”
“io/ioutil”
“net/http”
“os”
“path”
“strings”
"go-zero-micro/api/code/ucenterapi/internal/svc"
"go-zero-micro/api/code/ucenterapi/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(request *http.Request, req *types.FileUploadReq) (resp *types.BaseModel, err error) {
// todo: add your logic here and delete this line
//return LocalFileToByte(l, request, requestBody)
return FileToByte(l, request, req)
}
// LocalFileToByte 方式1:ioutil.ReadFile()转换成byte需要知道文件路径,因此会生成临时文件,不适合处理文件上传的场景
func LocalFileToByte(l *FileUploadLogic, request *http.Request, requestBody *types.FileUploadReq) (resp *types.BaseModel, err error) {
SavePath := l.svcCtx.Config.UploadFile.SavePath //上传文件的存储路径
utils.CreateDir(SavePath)
files := request.MultipartForm.File[“fileList”]
res := &types.BaseModel{
Data: “上传成功”,
}
param := &ucenter.FileList{}
copier.Copy(param, requestBody)
rpcFileList := make([]*ucenter.FileInfo, 0)
typeId := fmt.Sprintf(“%d”, requestBody.Type)
// 遍历所有文件
for _, fileHeader := range files {
//获取文件大小
fileSize := fileHeader.Size
//获取文件名称带后缀
fileNameWithSuffix := path.Base(fileHeader.Filename)
//获取文件的后缀(文件类型)
fileType := path.Ext(fileNameWithSuffix)
//生成UUID防止文件被覆盖
uuidName := typeId + “_” + strings.Replace(uuid.NewV4().String(), “-”, “”, -1)
saveName := uuidName + fileType
saveFullPath := SavePath + saveName
logx.Infof("upload file: %+v, file size: %d", fileNameWithSuffix, fileSize)
file, err := fileHeader.Open()
tempFile, err := os.Create(saveFullPath)
if err != nil {
return nil, err
}
io.Copy(tempFile, file)
//关闭文件
file.Close()
tempFile.Close()
//方式1:ioutil.ReadFile()转换成byte需要知道文件路径,不适合处理文件上传的场景
content, err := ioutil.ReadFile(saveFullPath)
fileInfo := &ucenter.FileInfo{
FileId: requestBody.Id,
FileName: fileNameWithSuffix,
FileType: fileType,
FileSize: fileSize,
FileData: content,
}
err = os.Remove(saveFullPath)
if err != nil {
logx.Infof("%s:删除失败", fileNameWithSuffix)
}
rpcFileList = append(rpcFileList, fileInfo)
}
param.File = rpcFileList
uploadRes, err := l.svcCtx.FileStorageRpc.FileUpload(l.ctx, param)
if err != nil {
return nil, err
}
res.Data = uploadRes.Data
return res, nil
}
// FileToByte 方式2:转换成byte适合上传文件的场景
func FileToByte(l *FileUploadLogic, request *http.Request, requestBody *types.FileUploadReq) (resp *types.BaseModel, err error) {
files := request.MultipartForm.File[“fileList”]
res := &types.BaseModel{
Data: “上传成功”,
}
param := &ucenter.FileList{}
copier.Copy(param, requestBody)
rpcFileList := make([]*ucenter.FileInfo, 0)
// 遍历所有文件
for _, fileHeader := range files {
//获取文件大小
fileSize := fileHeader.Size
//获取文件名称带后缀
fileNameWithSuffix := path.Base(fileHeader.Filename)
//获取文件的后缀(文件类型)
fileType := path.Ext(fileNameWithSuffix)
logx.Infof(“upload file: %+v, file size: %d”, fileNameWithSuffix, fileSize)
file, err := fileHeader.Open()
if err != nil {
return nil, err
}
//方式2:转换成byte适合上传文件的场景
fil := make([][]byte, 0)
var b int64 = 0
// 通过for循环写入
for {
buffer := make([]byte, 1024)
n, err := file.ReadAt(buffer, b)
b = b + int64(n)
fil = append(fil, buffer)
if err != nil {
fmt.Println(err.Error())
break
}
}
// 生成最后的文件字节流
content := bytes.Join(fil, []byte(“”))
fileInfo := &ucenter.FileInfo{
FileId: requestBody.Id,
FileName: fileNameWithSuffix,
FileType: fileType,
FileSize: fileSize,
FileData: content,
}
rpcFileList = append(rpcFileList, fileInfo)
}
param.File = rpcFileList
uploadRes, err := l.svcCtx.FileStorageRpc.FileUpload(l.ctx, param)
if err != nil {
return nil, err
}
res.Data = uploadRes.Data
return res, nil
}
6. 文件下载功能代码位置:`internal/logic/fileStorage/filedownloadlogic.go`,下载是写入到响应流中的,所以需要加入 `writer http.ResponseWriter`。
package fileStorage
import (
“context”
“errors”
“github.com/jinzhu/copier”
“go-zero-micro/api/code/ucenterapi/internal/svc”
“go-zero-micro/api/code/ucenterapi/internal/types”
“go-zero-micro/rpc/code/ucenter/ucenter”
“net/http”
"github.com/zeromicro/go-zero/core/logx"
)
type FileDownloadLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
writer http.ResponseWriter
}
func NewFileDownloadLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *FileDownloadLogic {
return &FileDownloadLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
writer: writer,
}
}
func (l *FileDownloadLogic) FileDownload(req *types.FileShowReq) error {
// todo: add your logic here and delete this line
param := &ucenter.FileInfo{}
copier.Copy(param, req)
downloadRes, err := l.svcCtx.FileStorageRpc.FileDownload(l.ctx, param)
if err != nil {
return errors.New(“文件服务异常”)
}
fileInfo, err := downloadRes.Recv()
if err != nil {
return errors.New(“文件下载失败”)
}
fileName := fileInfo.FileName
byteArr := fileInfo.FileData
//如果是下载,则需要在Header中设置这两个参数
//l.writer.Header().Add("Content-Type", "application/octet-stream")
//l.writer.Header().Add("Content-Disposition", "attachment; filename= "+fileName)
l.writer.Header().Add("Content-Type", "application/octet-stream")
l.writer.Header().Add("Content-Disposition", "attachment; filename= "+fileName)
l.writer.Write(byteArr)
return nil
}
7. 文件预览功能代码位置:`internal/logic/fileStorage/filepreviewlogic.go`(预览只针对浏览器支持的类型有效),预览和下载一样是写入到响应流中的,所以也需要加入 `writer http.ResponseWriter`。
package fileStorage
import (
“context”
“errors”
“github.com/jinzhu/copier”
“go-zero-micro/rpc/code/ucenter/ucenter”
“net/http”
"go-zero-micro/api/code/ucenterapi/internal/svc"
"go-zero-micro/api/code/ucenterapi/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type FilePreviewLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
writer http.ResponseWriter
}
func NewFilePreviewLogic(ctx context.Context, svcCtx *svc.ServiceContext, writer http.ResponseWriter) *FilePreviewLogic {
return &FilePreviewLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
writer: writer,
}
}
func (l *FilePreviewLogic) FilePreview(req *types.FileShowReq) error {
// todo: add your logic here and delete this line
param := &ucenter.FileInfo{}
copier.Copy(param, req)
downloadRes, err := l.svcCtx.FileStorageRpc.FileDownload(l.ctx, param)
if err != nil {
return errors.New(“文件服务异常”)
}
fileInfo, err := downloadRes.Recv()
if err != nil {
return errors.New(“打开文件失败”)
}
//fileName := fileInfo.FileName
byteArr := fileInfo.FileData
//如果是下载,则需要在Header中设置这两个参数
//l.writer.Header().Add("Content-Type", "application/octet-stream")
//l.writer.Header().Add("Content-Disposition", "attachment; filename= "+fileName)
l.writer.Write(byteArr)
return nil
}
**3 功能测试**
1. 文件上传功能
请求地址:<http://localhost:8888/fileStorage/fileUpload>
请求方式:POST
请求格式:FORM
请求数据:id=1,type=1,fileList(文件类型)
返回结果:
{
“code”: 200,
“msg”: “success”,
“success”: true,
“data”: {
“id”: 0,
“name”: “”,
“data”: “upload success”
}
}
2. 文件下载功能
请求地址:<http://127.0.0.1:8888/fileStorage/fileDownload?id=1&fileUrl=d3d22404926349fd8cb1924b12cadae8.png>
请求方式:GET
请求格式:URL
请求数据:id=1&fileUrl=d3d22404926349fd8cb1924b12cadae8.png
返回结果:请求成功后会立即下载。
3. 文件预览功能
请求地址:<http://127.0.0.1:8888/fileStorage/filePreview?id=1&fileUrl=d3d22404926349fd8cb1924b12cadae8.png>
请求方式:GET
请求格式:URL
请求数据:id=1&fileUrl=d3d22404926349fd8cb1924b12cadae8.png
返回结果:请求成功后会立即显示在浏览器页面上。
**4 注意事项**
1. api服务调用rpc服务报错并返回错误信息:`unknown service ucenter.fileStorage`。
**原因**:在启动类中rpc里新加的文件存储相关服务没有注册到rpc服务中,如果启动类的所有代码一直是使用`goctl`的命令自动生成的,则可能不会出现这个问题。
**解决办法**:手动在启动类里加上注册文件存储相关服务的代码。
package main
import (
“flag”
“fmt”
filestorageServer “go-zero-micro/rpc/code/ucenter/internal/server/filestorage”
"go-zero-micro/rpc/code/ucenter/internal/config"
ucentergormServer "go-zero-micro/rpc/code/ucenter/internal/server/ucentergorm"
ucentersqlxServer "go-zero-micro/rpc/code/ucenter/internal/server/ucentersqlx"
"go-zero-micro/rpc/code/ucenter/internal/svc"
"go-zero-micro/rpc/code/ucenter/ucenter"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var configFile = flag.String(“f”, “conf/dev/rpc/ucenter.yaml”, “the config file”)
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
ucenter.RegisterUcenterSqlxServer(grpcServer, ucentersqlxServer.NewUcenterSqlxServer(ctx))
ucenter.RegisterUcenterGormServer(grpcServer, ucentergormServer.NewUcenterGormServer(ctx))
//新增的分组接口必须要在这里注册,根据proto生成时可能未新增,否则会报 unknown service ucenter.fileStorage
ucenter.RegisterFileStorageServer(grpcServer, filestorageServer.NewFileStorageServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
2. 文件上传请求超时
如果文件过大,在上传文件时可能会出现请求等待一段时间后没有返回响应信息。
**原因**:
(1)超时时间设置过短;
(2)api往rpc发送信息时,数据过大;
**解决办法**:
在 `2.5 go-zero超时时间`、`2.6节 grpc请求体大小限制`中讲解。
### 2.5 go-zero超时时间
[本次示例代码]( )
在处理请求时有可能出现时间过长的情况,go-zero设置了默认的超时时间`(单位是毫秒)`避免等待过长。
在go-zero微服务超时时间的设置有三处:
1. rpc服务处理grpc请求时(grpc服务端);
2. api服务调用rpc服务时(grpc客户端);
3. api服务处理http请求时;
其中1是在rpc服务中配置,2、3是在api服务中配置。**如果是单体服务,则只需要配置`3. api服务处理http请求时`即可。**
**以登录超时演示实际效果**:先配置rpc服务中超时,再配置api服务中的超时。
**1 rpc服务处理grpc请求时(grpc服务端):**
这里是在rpc服务的配置文件yaml中设置,注意`Timeout`的层级位置。
Name: ucenter.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: ucenter.rpc
#rpc处理超时时间 10秒
Timeout: 10000
#开启grpc调试模式
Mode: dev
JWT:
AccessSecret: 1a3201qa-8b3d-ed0a-05eb-2e9c9b74f6b7
AccessExpire: 86400
#文件
UploadFile:
MaxFileNum: 1000
MaxFileSize: 1048576000 # 1000MB
SavePath: projects/go-zero-micro/uploadFiles/
#日志配置
Log:
Mode: file
Path: log/go-zero-micro/ucenterpc
Level: error
Compress: true
KeepDays: 180
在 `zrpc.RpcClientConf` 中可以查看到`go-zero`设置的默认超时时间是`2000毫秒`:
// A RpcServerConf is a rpc server config.
RpcServerConf struct {
service.ServiceConf
ListenOn string
Etcd discov.EtcdConf `json:",optional,inherit"`
Auth bool `json:",optional"`
Redis redis.RedisKeyConf `json:",optional"`
StrictControl bool `json:",optional"`
// setting 0 means no timeout
Timeout int64 `json:",default=2000"`
CpuThreshold int64 `json:",default=900,range=[0:1000]"`
// grpc health check switch
Health bool `json:",default=true"`
Middlewares ServerMiddlewaresConf
}
rpc服务模拟登录逻辑处理超时,在`internal/logic/ucentersqlx/loginuserlogic.go`中设置睡眠10秒钟。
package ucentersqlxlogic
import (
“context”
“go-zero-micro/common/utils”
“time”
"go-zero-micro/rpc/code/ucenter/internal/svc"
"go-zero-micro/rpc/code/ucenter/ucenter"
"github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginUserLogic struct {
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
000毫秒`:
// A RpcServerConf is a rpc server config.
RpcServerConf struct {
service.ServiceConf
ListenOn string
Etcd discov.EtcdConf `json:",optional,inherit"`
Auth bool `json:",optional"`
Redis redis.RedisKeyConf `json:",optional"`
StrictControl bool `json:",optional"`
// setting 0 means no timeout
Timeout int64 `json:",default=2000"`
CpuThreshold int64 `json:",default=900,range=[0:1000]"`
// grpc health check switch
Health bool `json:",default=true"`
Middlewares ServerMiddlewaresConf
}
rpc服务模拟登录逻辑处理超时,在internal/logic/ucentersqlx/loginuserlogic.go
中设置睡眠10秒钟。
package ucentersqlxlogic
import (
"context"
"go-zero-micro/common/utils"
"time"
"go-zero-micro/rpc/code/ucenter/internal/svc"
"go-zero-micro/rpc/code/ucenter/ucenter"
"github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginUserLogic struct {
[外链图片转存中...(img-MWpxYa6i-1715835787446)]
[外链图片转存中...(img-BSUqmLCw-1715835787447)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**