白嫖gradio连接分享

背景

这些天出现了不少好玩的ai项目,俺也是折腾的好不快乐。项目玩着玩着,发现清一色前端全是gradio写的,像SoVITS、ChatTTS啥的。gradio开箱即用,虽说和美观

大相径庭南辕北辙,但对于ai项目来说,项目能跑就行,为了前端美观不值当,有万能的社区呢。但咱今天重点不在于此,gradio在启动时如果设置下参数

share=True,浏览器便会打开个形如https://XXX.gradio.live的网址,你第一眼可能可能会有点懵,啥啥啥,这是个啥!鬼使神差的你访问了一下,嗨,这

玩意还真能访问,话说,它是咋把127.0.0.1夺舍的?嘿,这就得说道内网穿透了。

内网穿透

啥叫内网穿透?

我们查下百度百科,内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。

是不是感觉说的不是人话?我们翻译一下,就是在外面能访问家里的电脑。当然,gradio做的还没那么绝,只是原来这个项目只能局域网访问,现在这个限制不存在了。

这么好用的东西只能创建gradio项目使用,太暴殄天物了吧!俺也这么想的,整吧。

解决方案

我在寻求解决方案的时候,偶然发现个这个么项目gradio-tunneling,俺一拍大腿,嚯,还真是俺想要的,思路一看立马清晰明了。

刚出门便到了终点,这文章还写啥啊,按按delete吧。

我说服自己那是个python项目,很多人没python环境。脑袋里就有个人小人鄙视道,你就不能打个包,又没用啥大的库,python环境整进去能有多大,多少人电

脑上几百个浏览器了也没见抱怨。文件洁癖是病得改。

我把小人给摁住了,拿go练练手吧,命令行应用用go合适的不得了。

定义命令行

在go中命令行常用有两种,一种flag,还有一种cobra

flag是标准库,cobra是第三方库,这个项目怎么说呢,cobra是杀鸡用牛刀,flag整呗

// 定义命令行参数
portPtr := flag.Int("port", 8085, "定义要转发的端口")
address := flag.String("address", "https://api.gradio.app/v2/tunnel-request", "分享服务器地址")
binPath := flag.String("binPath", binaryPath, "frpc程序路径,默认查找可执行文件同级的bin目录")

再设置下帮助,就是输入-h时显示一大串参数那功能

flag.Usage = utils.PrintUsage

怎么显示呢,就把参数名是啥,功能是啥列出来

func PrintUsage() {
	fmt.Fprintf(flag.CommandLine.Output(), "使用方法: %s [选项]\n", flag.CommandLine.Name())
	fmt.Fprintln(flag.CommandLine.Output(), "选项:")
	flag.PrintDefaults()
}

设置frpc文件路径

gradio穿透就是利用frpc来实现的,我们需要定位frpc位置,至于为啥要定位,难道只想在windows上跑?那go语言的交叉编译不是白瞎了,frpc不同平台主程序肯定不一样啊,linux的剑斩windows的官,这个想法太大胆了。

frpc存放在同级bin目录之中,我们设置一下规则名称。

func GuessFrpcBinaryName() string {
	//判断当前系统平台
	platform := runtime.GOOS
	//架构
	arch := runtime.GOARCH
	return fmt.Sprintf("frpc_%s_%s", platform, arch)
}

这样我们就获取到了当前系统应该找啥路径。

binaryPath = fmt.Sprintf("bin/%s", fileName)

我们就可以对该路径该判断判断,该干嘛干嘛,当然,手动指定也是可以的

if *binPath != "" {
    binaryPath = *binPath
    //查询是否存在
    if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
        log.Fatalf(fmt.Sprintf("frpc 二进制文件路径不存在: %s\n", binaryPath))
        return
    }
} else {
    // 查询当前目录是否存在bin目录
    if _, err := os.Stat("bin"); os.IsNotExist(err) {
        log.Fatalf("frpc文件不存在: %s\n", binaryPath)
        return
    }
    //查询是否存在
    if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
        log.Fatalf("frpc文件不存在: %s\n", binaryPath)
        return
    }
}
//设置binaryPath为绝对路径
binaryPath, _ = filepath.Abs(binaryPath)

调用frpc还需要生成一个token

import (
	"crypto/rand"
	"encoding/base64"
)

func GenerateSecureToken(length int) (string, error) {
	b := make([]byte, length)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}

至此,万事俱备,该进入正题了

调用frpc

首先,定义一个结构体用于存放上述信息

type Tunnel struct {
	proc         *exec.Cmd
	FrpcPath     string
	RemoteHost   string
	RemotePort   int
	LocalHost    string
	LocalPort    int
	ShareToken   string
	stdoutReader io.ReadCloser
	stderrReader io.ReadCloser
}

构建命令

func (t *Tunnel) Start() (string, error) {
	cmdArgs := []string{
		"http",
		"-n",
		t.ShareToken,
		"-l",
		strconv.Itoa(t.LocalPort),
		"-i",
		t.LocalHost,
		"--uc",
		"--sd",
		"random",
		"--ue",
		"--server_addr",
		fmt.Sprintf("%s:%d", t.RemoteHost, t.RemotePort),
		"--disable_log_color",
	}
	t.proc = exec.Command(t.FrpcPath, cmdArgs...)
	log.Printf("cmd:%s\n", t.proc.String())

	...
	return url, nil
}

读取生产的公网地址

func (t *Tunnel) readURLFromTunnelStream(r io.Reader) (string, error) {
	log.Println("Reading from stream...")

	// Compile regex pattern once outside the loop for efficiency.
	re := regexp.MustCompile(`start proxy success: (.+)`)
	reader := bufio.NewReader(r)
	var url string
	var err error

	// Setup a single-use timer for timeout.
	timeout := time.After(30 * time.Second)

	// Read lines from the stream with timeout.
	for {
		select {
		case <-timeout:
			log.Println("Timeout occurred while reading from stream.")
			err = errors.New("read timeout")
			goto exit
		default:
			line, readErr := reader.ReadString('\n')
			if readErr != nil {
				if readErr != io.EOF { // Ignore EOF which can be a normal termination signal.
					err = readErr
				}
				goto exit
			}
			line = strings.TrimSpace(line) // Remove leading/trailing whitespaces.
			if line == "" {
				continue
			}
			log.Println("Read line:", line)
			if strings.Contains(line, "start proxy success") {
				matches := re.FindStringSubmatch(line)
				if len(matches) == 2 {
					url = matches[1]
					goto exit
				}
			} else if strings.Contains(line, "login to server failed") {
				err = errors.New("login to server failed")
				goto exit
			}
		}
	}

exit:
	log.Println("Read operation completed.")
	return url, err
}

打包

当然可以使用go build,简单快捷,但是,不试试更爽的gox

gox

安装

go get github.com/mitchellh/gox

我看很多教程安装完了就直接用了,我试了识别不了,这种情况下咋办

找到gox文件夹,找不到就git clone

C:\Users\{user}\go\pkg\mod\github.com\mitchellh

运行go build,将生成的gox.exe添加到环境变量

gox -output "build/gradio-tunnel_{{.OS}}_{{.Arch}}"

各个平台的包就吭哧吭呲都编译好了,还在程序名上做了区分

自动化打包

但咱还得把bin目录添加进去啊,还得压缩啊,整个全自动化吧

构建这东西还是python写方便,先来个压缩

import zipfile
import os

def zip(zip_path, files):
    """
    压缩文件至zip
    压缩文件至zip
    :param zip_path: [FILE]zip包路径-zip包路径
    :param files: [DIR]压缩目录-待压缩文件的目录 字符串和字符串数组均可
    """
    if isinstance(files, str):
        files = [files]

    with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
        for file in files:
            if os.path.isdir(file):
                for dir_path, dir_names, file_names in os.walk(file):
                    for filename in file_names:
                        file_path = dir_path.replace(file, '')
                        zf.write(os.path.join(dir_path, filename), file_path + '\\'+Path(file).name+"\\" + filename)
            elif os.path.isfile(file):
                path, file_name = os.path.split(file)
                zf.write(file, file_name)
            else:
                raise Exception('请检查路径%s' % file)

打包完将bin目录和主程序一起制作zip压缩包

def main():
    project_home = "D:\xxx"
    build_home = r"D:\build\xxx"
    if Path(build_home).exists():
        log.info("build_home exists")
        # 清空
        for file in Path(build_home).iterdir():
            if file.is_file():
                file.unlink()
                log.info("delete %s success" % file.name)
    else:
        log.info("build_home not exists")
        Path(build_home).mkdir(parents=True,exist_ok=True)

    # 编译gox
    result = subprocess.run(["gox","-output","%s/gradio-tunnel_{{.OS}}_{{.Arch}}" % build_home],cwd=project_home)
    if result.returncode == 0:
        log.info("gox compile success")
    else:
        log.error("gox compile failed")


    # 遍历build_home下的文件
    for file in Path(build_home).iterdir():
        if file.is_file():
            # 压缩文件
            zip_path = file.with_suffix(".zip")
            zip(zip_path, [file,project_home+"\\bin"], "")
            log.info("zip %s success" % file.name)
            # 删除文件
            file.unlink()
            log.info("delete %s success" % file.name)

齐活儿,下面就是各个平台测试了,精力有效,就试了试windows和linux,嗯,效果不错不错,白嫖的代理就是爽。

代码

本文代码已开源,感兴趣可参考,github创建了realease,下载即用。

Java连接Gradio客户端通常指的是在Java程序中使用Gradio的API或库来进行数据的发送与接收。Gradio是一个开源的机器学习模型演示平台,它允许用户通过简单的接口创建界面,并与后端的机器学习模型进行交互。在Java中连接Gradio客户端,你可以使用HTTP请求与Gradio服务进行通信。 以下是一个基本的步骤概述: 1. 获取Gradio服务的URL地址,这是你将要发送HTTP请求到的服务端点。 2. 根据Gradio服务的API文档,构建HTTP请求。这通常包括设置正确的请求方法(如GET或POST),以及必要的请求头和请求体。 3. 在Java程序中,可以使用各种库如HttpURLConnection或者Apache HttpClient来发送HTTP请求。 4. 发送请求后,接收并处理Gradio服务返回的响应数据。 下面是一个简单的示例代码,演示了如何使用Java的HttpURLConnection发送一个HTTP GET请求: ```java import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class GradioClientExample { public static void main(String[] args) { String gradioServiceUrl = "http://your_gradio_service_url"; // 替换成你的Gradio服务地址 try { URL url = new URL(gradioServiceUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("Content-Type", "application/json; utf-8"); connection.setRequestProperty("Accept", "application/json"); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line).append("\n"); } reader.close(); System.out.println("Response from Gradio: " + response.toString()); } else { System.out.println("GET request not worked"); } connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值