JeshuaRen 1 year ago
parent
commit
418471ed0c
13 changed files with 287 additions and 218 deletions
  1. +2
    -2
      pkgs/ioswitch/exec/driver.go
  2. +12
    -7
      pkgs/ioswitch/exec/executor.go
  3. +1
    -1
      pkgs/ioswitch/exec/plan_builder.go
  4. +21
    -24
      pkgs/ioswitch/exec/var.go
  5. +10
    -10
      pkgs/ioswitch/plan/ops/send.go
  6. +99
    -114
      sdks/storage/cdsapi/hub_io.go
  7. +87
    -0
      sdks/storage/cdsapi/utils.go
  8. +13
    -10
      sdks/storage/models.go
  9. +0
    -2
      sdks/storage/shard_storage.go
  10. +8
    -0
      sdks/storage/storage.go
  11. +13
    -47
      utils/http2/http.go
  12. +1
    -1
      utils/os2/dir_iterator.go
  13. +20
    -0
      utils/os2/utils.go

+ 2
- 2
pkgs/ioswitch/exec/driver.go View File

@@ -13,7 +13,7 @@ import (
type Driver struct {
planID PlanID
planBlder *PlanBuilder
callback *future.SetValueFuture[map[string]any]
callback *future.SetValueFuture[map[string]VarValue]
ctx *ExecContext
cancel context.CancelFunc
driverExec *Executor
@@ -42,7 +42,7 @@ func (e *Driver) Signal(signal *DriverSignalVar) {
e.driverExec.PutVar(signal.ID, &SignalValue{})
}

func (e *Driver) Wait(ctx context.Context) (map[string]any, error) {
func (e *Driver) Wait(ctx context.Context) (map[string]VarValue, error) {
stored, err := e.callback.Wait(ctx)
if err != nil {
return nil, err


+ 12
- 7
pkgs/ioswitch/exec/executor.go View File

@@ -15,19 +15,24 @@ type binding struct {
Callback *future.SetValueFuture[VarValue]
}

type freeVar struct {
ID VarID
Value VarValue
}

type Executor struct {
plan Plan
vars map[VarID]Var
vars map[VarID]freeVar
bindings []*binding
lock sync.Mutex
store map[string]any
store map[string]VarValue
}

func NewExecutor(plan Plan) *Executor {
planning := Executor{
plan: plan,
vars: make(map[VarID]Var),
store: make(map[string]any),
vars: make(map[VarID]freeVar),
store: make(map[string]VarValue),
}

return &planning
@@ -37,7 +42,7 @@ func (s *Executor) Plan() *Plan {
return &s.plan
}

func (s *Executor) Run(ctx *ExecContext) (map[string]any, error) {
func (s *Executor) Run(ctx *ExecContext) (map[string]VarValue, error) {
c, cancel := context.WithCancel(ctx.Context)
ctx.Context = c

@@ -99,11 +104,11 @@ func (s *Executor) PutVar(id VarID, value VarValue) *Executor {
}

// 如果没有绑定,则直接放入变量表中
s.vars[id] = Var{ID: id, Value: value}
s.vars[id] = freeVar{ID: id, Value: value}
return s
}

func (s *Executor) Store(key string, val any) {
func (s *Executor) Store(key string, val VarValue) {
s.lock.Lock()
defer s.lock.Unlock()



+ 1
- 1
pkgs/ioswitch/exec/plan_builder.go View File

@@ -63,7 +63,7 @@ func (b *PlanBuilder) Execute(ctx *ExecContext) *Driver {
exec := Driver{
planID: planID,
planBlder: b,
callback: future.NewSetValue[map[string]any](),
callback: future.NewSetValue[map[string]VarValue](),
ctx: ctx,
cancel: cancel,
driverExec: NewExecutor(execPlan),


+ 21
- 24
pkgs/ioswitch/exec/var.go View File

@@ -10,23 +10,11 @@ import (

type VarID int

type Var struct {
ID VarID `json:"id"`
Value VarValue `json:"value"`
}

type VarPack[T VarValue] struct {
type Var[T VarValue] struct {
ID VarID `json:"id"`
Value T `json:"value"`
}

func (v *VarPack[T]) ToAny() AnyVar {
return AnyVar{
ID: v.ID,
Value: v.Value,
}
}

// 变量的值
type VarValue interface {
Clone() VarValue
@@ -42,15 +30,6 @@ func UseVarValue[T VarValue]() {
valueUnion.Add(reflect2.TypeOf[T]())
}

type AnyVar = VarPack[VarValue]

func V(id VarID, value VarValue) AnyVar {
return AnyVar{
ID: id,
Value: value,
}
}

type StreamValue struct {
Stream io.ReadCloser `json:"-"`
}
@@ -60,15 +39,24 @@ func (v *StreamValue) Clone() VarValue {
panic("StreamValue should not be cloned")
}

type StreamVar = Var[*StreamValue]

func NewStreamVar(id VarID, stream io.ReadCloser) StreamVar {
return StreamVar{
ID: id,
Value: &StreamValue{Stream: stream},
}
}

type SignalValue struct{}

func (o *SignalValue) Clone() VarValue {
return &SignalValue{}
}

type SignalVar = VarPack[*SignalValue]
type SignalVar = Var[*SignalValue]

func NewSignal(id VarID) SignalVar {
func NewSignalVar(id VarID) SignalVar {
return SignalVar{
ID: id,
Value: &SignalValue{},
@@ -82,3 +70,12 @@ type StringValue struct {
func (o *StringValue) Clone() VarValue {
return &StringValue{Value: o.Value}
}

type StringVar = Var[*StringValue]

func NewStringVar(id VarID, value string) StringVar {
return StringVar{
ID: id,
Value: &StringValue{Value: value},
}
}

+ 10
- 10
pkgs/ioswitch/plan/ops/send.go View File

@@ -50,10 +50,10 @@ func (o *SendStream) String() string {
}

type GetStream struct {
Signal exec.VarPack[*exec.SignalValue] `json:"signal"`
Target exec.VarID `json:"target"`
Output exec.VarID `json:"output"`
Worker exec.WorkerInfo `json:"worker"`
Signal exec.SignalVar `json:"signal"`
Target exec.VarID `json:"target"`
Output exec.VarID `json:"output"`
Worker exec.WorkerInfo `json:"worker"`
}

func (o *GetStream) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
@@ -113,10 +113,10 @@ func (o *SendVar) String() string {
}

type GetVar struct {
Signal exec.VarPack[*exec.SignalValue] `json:"signal"`
Target exec.VarID `json:"target"`
Output exec.VarID `json:"output"`
Worker exec.WorkerInfo `json:"worker"`
Signal exec.SignalVar `json:"signal"`
Target exec.VarID `json:"target"`
Output exec.VarID `json:"output"`
Worker exec.WorkerInfo `json:"worker"`
}

func (o *GetVar) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
@@ -234,7 +234,7 @@ func (t *GetStreamNode) SignalVar() *dag.Var {

func (t *GetStreamNode) GenerateOp() (exec.Op, error) {
return &GetStream{
Signal: exec.NewSignal(t.OutputValues().Get(0).VarID),
Signal: exec.NewSignalVar(t.OutputValues().Get(0).VarID),
Output: t.OutputStreams().Get(0).VarID,
Target: t.InputStreams().Get(0).VarID,
Worker: t.FromWorker,
@@ -273,7 +273,7 @@ func (t *GetValueNode) SignalVar() *dag.Var {

func (t *GetValueNode) GenerateOp() (exec.Op, error) {
return &GetVar{
Signal: exec.NewSignal(t.OutputValues().Get(0).VarID),
Signal: exec.NewSignalVar(t.OutputValues().Get(0).VarID),
Output: t.OutputValues().Get(1).VarID,
Target: t.InputValues().Get(0).VarID,
Worker: t.FromWorker,


+ 99
- 114
sdks/storage/cdsapi/hub_io.go View File

@@ -1,11 +1,8 @@
package cdsapi

import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"

@@ -26,31 +23,32 @@ type GetStreamReq struct {
Signal exec.VarValue `json:"signal"`
}

func (c *Client) GetStream(planID exec.PlanID, id exec.VarID, signalID exec.VarID, signal exec.VarValue) (io.ReadCloser, error) {
func (c *Client) GetStream(req GetStreamReq) (io.ReadCloser, error) {
targetUrl, err := url.JoinPath(c.baseURL, GetStreamPath)
if err != nil {
return nil, err
}

req := &GetStreamReq{
PlanID: planID,
VarID: id,
SignalID: signalID,
Signal: signal,
body, err := serder.ObjectToJSONEx(req)
if err != nil {
return nil, fmt.Errorf("request to json: %w", err)
}

resp, err := http2.GetJSON(targetUrl, http2.RequestParam{
Body: req,
Body: body,
})
if err != nil {
return nil, err
}

if resp.StatusCode != http.StatusOK {
// 读取错误信息
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return nil, fmt.Errorf("error response from server: %s", string(body))
contType := resp.Header.Get("Content-Type")
if strings.Contains(contType, http2.ContentTypeJSON) {
var codeResp response[any]
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
return nil, fmt.Errorf("parsing response: %w", err)
}

return nil, codeResp.ToError()
}

return resp.Body, nil
@@ -59,60 +57,54 @@ func (c *Client) GetStream(planID exec.PlanID, id exec.VarID, signalID exec.VarI
const SendStreamPath = "/hubIO/sendStream"

type SendStreamReq struct {
PlanID exec.PlanID `json:"planID"`
VarID exec.VarID `json:"varID"`
Stream io.ReadCloser `json:"stream"`
SendStreamInfo
Stream io.ReadCloser `json:"-"`
}
type SendStreamInfo struct {
PlanID exec.PlanID `json:"planID"`
VarID exec.VarID `json:"varID"`
}

func (c *Client) SendStream(planID exec.PlanID, varID exec.VarID, str io.Reader) error {
targetUrl, err := url.JoinPath(c.baseURL, SendStreamPath)
if err != nil {
return err
}

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)

_ = writer.WriteField("plan_id", string(planID))
_ = writer.WriteField("var_id", string(rune(varID)))
fileWriter, err := writer.CreateFormFile("file", "data")
if err != nil {
return fmt.Errorf("creating form file: %w", err)
}

// 将读取的数据写入 multipart 的文件部分
_, err = io.Copy(fileWriter, str)
if err != nil {
return fmt.Errorf("copying stream data: %w", err)
}

// 关闭 writer 以结束 multipart
err = writer.Close()
if err != nil {
return fmt.Errorf("closing writer: %w", err)
}

// 创建 HTTP 请求
req, err := http.NewRequest("POST", targetUrl, body)
if err != nil {
return fmt.Errorf("creating HTTP request: %w", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())

// 发送请求
cli := http.Client{}
resp, err := cli.Do(req)
if err != nil {
return fmt.Errorf("sending HTTP request: %w", err)
}
defer resp.Body.Close()

// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("server returned non-200 status: %d", resp.StatusCode)
}

return nil
func (c *Client) SendStream(req SendStreamReq) error {
// targetUrl, err := url.JoinPath(c.baseURL, SendStreamPath)
// if err != nil {
// return err
// }

// infoJSON, err := serder.ObjectToJSON(req)
// if err != nil {
// return fmt.Errorf("info to json: %w", err)
// }

// resp, err := http2.PostMultiPart(targetUrl, http2.MultiPartRequestParam{
// Form: map[string]string{"info": string(infoJSON)},
// Files: iterator.Array(&http2.IterMultiPartFile{
// FieldName: "stream",
// FileName: "stream",
// File: req.Stream,
// }),
// })
// if err != nil {
// return err
// }

// contType := resp.Header.Get("Content-Type")
// if strings.Contains(contType, http2.ContentTypeJSON) {
// var err error
// var codeResp response[ObjectUploadResp]
// if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadResp]](resp.Body); err != nil {
// return fmt.Errorf("parsing response: %w", err)
// }

// if codeResp.Code == errorcode.OK {
// return nil
// }

// return codeResp.ToError()
// }

// return fmt.Errorf("unknow response content type: %s", contType)
return fmt.Errorf("not implemented")
}

const ExecuteIOPlanPath = "/hubIO/executeIOPlan"
@@ -121,38 +113,34 @@ type ExecuteIOPlanReq struct {
Plan exec.Plan `json:"plan"`
}

func (c *Client) ExecuteIOPlan(plan exec.Plan) error {
func (c *Client) ExecuteIOPlan(req ExecuteIOPlanReq) error {
targetUrl, err := url.JoinPath(c.baseURL, ExecuteIOPlanPath)
if err != nil {
return err
}

req := &ExecuteIOPlanReq{
Plan: plan,
body, err := serder.ObjectToJSONEx(req)
if err != nil {
return fmt.Errorf("request to json: %w", err)
}

resp, err := http2.PostJSON(targetUrl, http2.RequestParam{
Body: req,
Body: body,
})
if err != nil {
return err
}

contType := resp.Header.Get("Content-Type")
if strings.Contains(contType, http2.ContentTypeJSON) {
var codeResp response[any]
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
return fmt.Errorf("parsing response: %w", err)
}

if codeResp.Code == errorcode.OK {
return nil
}
codeResp, err := ParseJSONResponse[response[any]](resp)
if err != nil {
return err
}

return codeResp.ToError()
if codeResp.Code == errorcode.OK {
return nil
}

return fmt.Errorf("unknow response content type: %s", contType)
return codeResp.ToError()
}

const SendVarPath = "/hubIO/sendVar"
@@ -163,40 +151,34 @@ type SendVarReq struct {
VarValue exec.VarValue `json:"varValue"`
}

func (c *Client) SendVar(planID exec.PlanID, id exec.VarID, value exec.VarValue) error {
func (c *Client) SendVar(req SendVarReq) error {
targetUrl, err := url.JoinPath(c.baseURL, SendVarPath)
if err != nil {
return err
}

req := &SendVarReq{
PlanID: planID,
VarID: id,
VarValue: value,
body, err := serder.ObjectToJSONEx(req)
if err != nil {
return fmt.Errorf("request to json: %w", err)
}

resp, err := http2.PostJSON(targetUrl, http2.RequestParam{
Body: req,
Body: body,
})
if err != nil {
return err
}

contType := resp.Header.Get("Content-Type")
if strings.Contains(contType, http2.ContentTypeJSON) {
var codeResp response[any]
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
return fmt.Errorf("parsing response: %w", err)
}

if codeResp.Code == errorcode.OK {
return nil
}
jsonResp, err := ParseJSONResponse[response[any]](resp)
if err != nil {
return err
}

return codeResp.ToError()
if jsonResp.Code == errorcode.OK {
return nil
}

return fmt.Errorf("unknow response content type: %s", contType)
return jsonResp.ToError()
}

const GetVarPath = "/hubIO/getVar"
@@ -208,33 +190,36 @@ type GetVarReq struct {
Signal exec.VarValue `json:"signal"`
}

func (c *Client) GetVar(id exec.PlanID, varID exec.VarID, singalID exec.VarID, signal exec.VarValue) (exec.VarValue, error) {
type GetVarResp struct {
Value exec.VarValue `json:"value"`
}

func (c *Client) GetVar(req GetVarReq) (*GetVarResp, error) {
targetUrl, err := url.JoinPath(c.baseURL, GetVarPath)
if err != nil {
return nil, err
}

req := &GetVarReq{
PlanID: id,
VarID: varID,
SignalID: singalID,
Signal: signal,
body, err := serder.ObjectToJSONEx(req)
if err != nil {
return nil, fmt.Errorf("request to json: %w", err)
}

resp, err := http2.GetJSON(targetUrl, http2.RequestParam{
Body: req,
Body: body,
})
if err != nil {
return nil, err
}

body, _ := io.ReadAll(resp.Body)
jsonResp, err := ParseJSONResponse[response[GetVarResp]](resp)
if err != nil {
return nil, err
}

if resp.StatusCode != http.StatusOK {
// 读取错误信息
resp.Body.Close()
return nil, fmt.Errorf("error response from server: %s", string(body))
if jsonResp.Code == errorcode.OK {
return &jsonResp.Data, nil
}

return nil
return nil, jsonResp.ToError()
}

+ 87
- 0
sdks/storage/cdsapi/utils.go View File

@@ -1,6 +1,7 @@
package cdsapi

import (
"encoding/binary"
"fmt"
"io"
"net/http"
@@ -8,6 +9,7 @@ import (
"strings"

"gitlink.org.cn/cloudream/common/utils/http2"
"gitlink.org.cn/cloudream/common/utils/io2"
"gitlink.org.cn/cloudream/common/utils/math2"
"gitlink.org.cn/cloudream/common/utils/serder"
)
@@ -36,3 +38,88 @@ func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) {

return ret, fmt.Errorf("unknow response content type: %s, status: %d, body(prefix): %s", contType, resp.StatusCode, strCont[:math2.Min(len(strCont), 200)])
}

func WriteStream(dst io.Writer, src io.Reader) (int64, error) {
sent := int64(0)

buf := make([]byte, 1024*4)
header := make([]byte, 4)

for {
n, err := src.Read(buf)
if n > 0 {
binary.LittleEndian.PutUint32(header, uint32(n))
err := io2.WriteAll(dst, header)
if err != nil {
return sent, err
}

sent += int64(n)
}

if err == io.EOF {
binary.LittleEndian.PutUint32(header, uint32(0))
err := io2.WriteAll(dst, header)
if err != nil {
return sent, err
}
return sent, nil
}

if err != nil {
errData := []byte(err.Error())
header := make([]byte, 4)
binary.LittleEndian.PutUint32(header, uint32(-len(errData)))
// 不管有没有成功
io2.WriteAll(dst, header)
io2.WriteAll(dst, errData)
return sent, err
}
}
}

func ReadStream(src io.Reader) io.ReadCloser {
pr, pw := io.Pipe()

buf := make([]byte, 1024*4)
go func() {
for {
_, err := io.ReadFull(src, buf[:4])
if err != nil {
pw.CloseWithError(err)
break
}

h := int32(binary.LittleEndian.Uint32(buf[:4]))
if h == 0 {
pw.Close()
break
}

if h < 0 {
_, err := io.ReadFull(src, buf[:-h])
if err != nil {
pw.CloseWithError(err)
break
}

pw.CloseWithError(fmt.Errorf(string(buf[:-h])))
break
}

_, err = io.ReadFull(src, buf[:h])
if err != nil {
pw.CloseWithError(err)
break
}

_, err = pw.Write(buf[:h])
if err != nil {
pw.Close()
break
}
}
}()

return pr
}

+ 13
- 10
sdks/storage/models.go View File

@@ -27,6 +27,9 @@ type StorageID int64

type LocationID int64

// 文件的SHA256哈希值,全大写的16进制字符串格式
type FileHash string

/// TODO 将分散在各处的公共结构体定义集中到这里来

type Redundancy interface {
@@ -176,7 +179,7 @@ type Object struct {
PackageID PackageID `db:"PackageID" json:"packageID"`
Path string `db:"Path" json:"path"`
Size int64 `db:"Size" json:"size,string"`
FileHash string `db:"FileHash" json:"fileHash"`
FileHash FileHash `db:"FileHash" json:"fileHash"`
Redundancy Redundancy `db:"Redundancy" json:"redundancy"`
CreateTime time.Time `db:"CreateTime" json:"createTime"`
UpdateTime time.Time `db:"UpdateTime" json:"updateTime"`
@@ -245,21 +248,21 @@ type NodeConnectivity struct {
TestTime time.Time `db:"TestTime" json:"testTime"`
}

type NodePackageCachingInfo struct {
NodeID NodeID `json:"nodeID"`
FileSize int64 `json:"fileSize"`
ObjectCount int64 `json:"objectCount"`
type StoragePackageCachingInfo struct {
StorageID StorageID `json:"storageID"`
FileSize int64 `json:"fileSize"`
ObjectCount int64 `json:"objectCount"`
}

type PackageCachingInfo struct {
NodeInfos []NodePackageCachingInfo `json:"nodeInfos"`
PackageSize int64 `json:"packageSize"`
StorageInfos []StoragePackageCachingInfo `json:"stgInfos"`
PackageSize int64 `json:"packageSize"`
}

func NewPackageCachingInfo(nodeInfos []NodePackageCachingInfo, packageSize int64) PackageCachingInfo {
func NewPackageCachingInfo(stgInfos []StoragePackageCachingInfo, packageSize int64) PackageCachingInfo {
return PackageCachingInfo{
NodeInfos: nodeInfos,
PackageSize: packageSize,
StorageInfos: stgInfos,
PackageSize: packageSize,
}
}



+ 0
- 2
sdks/storage/shard_storage.go View File

@@ -20,8 +20,6 @@ var _ = serder.UseTypeUnionInternallyTagged(types.Ref(types.NewTypeUnion[ShardSt

type ShardStorage struct {
StorageID StorageID `json:"storageID" gorm:"column:StorageID; primaryKey"`
// 完全管理此存储服务的Hub的ID
MasterHub NodeID `json:"masterHub" gorm:"column:MasterHub; not null"`
// Shard存储空间在存储服务的目录
Root string `json:"root" gorm:"column:Root; not null"`
// ShardStore配置数据


+ 8
- 0
sdks/storage/storage.go View File

@@ -1,6 +1,8 @@
package cdssdk

import (
"fmt"

"gitlink.org.cn/cloudream/common/pkgs/types"
"gitlink.org.cn/cloudream/common/utils/serder"
)
@@ -31,12 +33,18 @@ func (a *LocalStorageAddress) String() string {
type Storage struct {
StorageID StorageID `json:"storageID" gorm:"column:StorageID; primaryKey; autoIncrement;"`
Name string `json:"name" gorm:"column:Name; not null"`
// 完全管理此存储服务的Hub的ID
MasterHub NodeID `json:"masterHub" gorm:"column:MasterHub; not null"`
// 存储服务的地址,包含鉴权所需数据
Address StorageAddress `json:"address" gorm:"column:Address; type:json; not null; serializer:union"`
// 存储服务拥有的特别功能
Features []StorageFeature `json:"features" gorm:"column:Features; type:json; serializer:union"`
}

func (s *Storage) String() string {
return fmt.Sprintf("%v(%v)", s.Name, s.StorageID)
}

// 共享存储服务的配置数据
type SharedStorage struct {
StorageID StorageID `json:"storageID" gorm:"column:StorageID; primaryKey"`


+ 13
- 47
utils/http2/http.go View File

@@ -32,7 +32,7 @@ var defaultClient = http.DefaultClient
type RequestParam struct {
Header any
Query any
Body any
Body any // 如果是[]byte,则直接作为请求体,否则会被序列化等处理
}

func GetJSON(url string, param RequestParam) (*http.Response, error) {
@@ -119,45 +119,6 @@ func PostJSON(url string, param RequestParam) (*http.Response, error) {
return defaultClient.Do(req)
}

func PostJSONRow(url string, param RequestParam) (*http.Response, error) {
req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
return nil, err
}

if err = prepareQuery(req, param.Query); err != nil {
return nil, err
}

if err = prepareHeader(req, param.Header); err != nil {
return nil, err
}

//if err = prepareJSONBody(req, param.Body); err != nil {
// return nil, err
//}

setHeader(req.Header, "Content-Type", ContentTypeJSON)

if param.Body == nil {
return nil, nil
}

switch body := param.Body.(type) {
case nil:
case string:
req.ContentLength = int64(len(body))
req.Body = io.NopCloser(bytes.NewReader([]byte(body)))
case []byte:
req.ContentLength = int64(len(body))
req.Body = io.NopCloser(bytes.NewReader(body))
default:
return nil, fmt.Errorf("body error")
}

return defaultClient.Do(req)
}

func PostForm(url string, param RequestParam) (*http.Response, error) {
req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
@@ -528,17 +489,22 @@ func prepareHeader(req *http.Request, header any) error {
func prepareJSONBody(req *http.Request, body any) error {
setHeader(req.Header, "Content-Type", ContentTypeJSON)

if body == nil {
switch body := body.(type) {
case nil:
return nil
}
case []byte:
req.ContentLength = int64(len(body))
req.Body = io.NopCloser(bytes.NewReader(body))
default:
data, err := serder.ObjectToJSON(body)
if err != nil {
return err
}

data, err := serder.ObjectToJSON(body)
if err != nil {
return err
req.ContentLength = int64(len(data))
req.Body = io.NopCloser(bytes.NewReader(data))
}

req.ContentLength = int64(len(data))
req.Body = io.NopCloser(bytes.NewReader(data))
return nil
}



utils/os/dir_iterator.go → utils/os2/dir_iterator.go View File

@@ -1,4 +1,4 @@
package os
package os2

import (
"os"

+ 20
- 0
utils/os2/utils.go View File

@@ -0,0 +1,20 @@
package os2

import (
"math/rand"
"strings"
)

func GenerateRandomFileName(len int) string {
sb := strings.Builder{}
for i := 0; i < len; i++ {
rd := rand.Intn(26 + 10)
if rd < 26 {
sb.WriteRune('a' + rune(rd))
} else {
sb.WriteRune('0' + rune(rd-10))
}
}

return sb.String()
}

Loading…
Cancel
Save