stripe一体化全球支付平台，有良好的用户体验和界面设计，支持多种接入方式，且功能比较灵活，也易于集成。注册和API密钥账户注册验证激活访问Stripe网站：https://stripe.com/zh-hk ，并点击登录按钮登录，没有账号的可以先注册一个。填写必要信息：提供您的电子邮件地址、密码等信息以注册账户，并完成身份验证步骤，以确保账户的安全性。集成stripe支付，一开始账户处于开发模式，所有数据都是模拟的，上线时录入商家或公司信息激活账户进入真实模式。API密钥获取登录Stripe控制台：https://dashboard.stripe.com ，使用您的账户信息登录Stripe控制台。进入开发人员界面，单击API密钥选项卡，在下面可以找到公钥和密钥。在开发阶段，使用测试密钥，在生产环境中使用生产密钥，注意保管好密钥。妥善保管这些凭据，并根据需要定期更新它们，以维护支付系统的安全性集成前后端这里使用go语言为后端语言，react为前端语言。一、设置服务器将依赖项添加到您的项目并导入库。go get -u github.com/stripe/stripe-go/v78接下来创建一个结账接口，前端客户请求这个接口时，按客户的要购买的商品来创建一个结账会话，并将这个会话的ClientSecret返回给客户。import ( "log" "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/stripe/stripe-go/v78" "github.com/stripe/stripe-go/v78/checkout/session" ) func init() { // This is your test secret API key. stripe.Key = "sk_test_51P12rrI3yx14f44QTHOPqliw9So8GBpZ6EIPi7v1iDbdlgr5NH0vXIH3zUjQJwxLI7pbAsfSErQOy75sAVr7q4ra00AODgXKD0" } func CreateCheckoutSession(c *gin.Context) { var req struct { Email string `json:"email"` Amount int64 `json:"amount"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "msg": "param decode error:" + err.Error(), }) } params := &stripe.CheckoutSessionParams{ LineItems: []*stripe.CheckoutSessionLineItemParams{ { PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("usd"), UnitAmount: stripe.Int64(10000 * req.Amount), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("AI Tools"), }, }, Quantity: stripe.Int64(1), }, }, CustomerEmail: stripe.String(req.Email), Metadata: map[string]string{"email": req.Email, "amount": strconv.Itoa(int(req.Amount))}, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), UIMode: stripe.String("embedded"), ReturnURL: stripe.String("http://localhost:5173/#/payment?session_id={CHECKOUT_SESSION_ID}"), } s, err := session.New(params) if err != nil { log.Printf("session.New: %v", err) } c.JSON(http.StatusOK, gin.H{ "clientSecret": s.ClientSecret, }) }按客户实际请求填充stripe.CheckoutSessionParams，可以参考上面代码，修改产品和价格即可。另外需要一个会话详情接口，用来辅助前端查看结账会话是否已经支付。func RetrieveCheckoutSession(c *gin.Context) { s, _ := session.Get(c.Query("session_id"), nil) c.JSON(http.StatusOK, gin.H{ "status": string(s.Status), "customer_email": string(s.CustomerDetails.Email), }) }最后再配置路由端口 router := gin.Default() router.POST("/create-checkout-session", CreateCheckoutSession) router.GET("/session-status", RetrieveCheckoutSession)二、设置前端将Stripe添加到React应用程序中npm install --save @stripe/react-stripe-js @stripe/stripe-js编写一个支付组件和结果组件，要付款时展示支付组件，支付完成后展示结果组件，调用后端结账接口完成支付功能。import { Button, Card, Checkbox, Form, Input, InputNumber, message, theme } from 'antd' import { useCallback, useEffect, useState } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { Api } from '@api/index' import { loadStripe } from '@stripe/stripe-js'; import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js'; import { getLocalStorage } from '@utils/publicFn' const stripePromise = loadStripe("pk_test_51P12rrI3yx14f44QGrlx7wtiyuwt969ttal5rfsSKOqxvoL3iUt3QlTft9b05ZFNea6KQvSriOeESbbBbJOba6nN00LXBpdQ9r"); const CheckoutForm = ({ email, amount }) => { const fetchClientSecret = useCallback(async () => { return (await Api.createCheckoutSession({ email, amount })).clientSecret }, []); return ( <EmbeddedCheckoutProvider stripe={stripePromise} options={{ fetchClientSecret }} > <EmbeddedCheckout /> </EmbeddedCheckoutProvider> ) } const Complete = ({ customerEmail }) => { const navigate = useNavigate(); return ( We appreciate your business! A confirmation email will be sent to {customerEmail}. If you have any questions, please email orders@example.com. navigate('/payment')}>返回 ) } const Payment = () => { const navigate = useNavigate(); const { token: { colorBgContainer }, } = theme.useToken() const [open, setOpen] = useState(false); const [status, setStatus] = useState(null); const [expiration, setExpiration] = useState(0); const [customerEmail, setCustomerEmail] = useState(''); const [email, setEmail] = useState(getLocalStorage('user')); const [amount, setAmount] = useState(1); const queryString = useLocation().search; useEffect(() => { Api.userProfile() .then((data) => { setExpiration(data.expiration) }) }, []) useEffect(() => { const urlParams = new URLSearchParams(queryString); const sessionId = urlParams.get('session_id'); if (sessionId) { Api.sessionStatus({ session_id: sessionId }) .then((data) => { setStatus(data.status); setCustomerEmail(data.customer_email); }); } else { setStatus(null); setCustomerEmail(''); setOpen(false) } }, [queryString]); const handlePay = ({ email, amount }) => { setEmail(email) setAmount(parseInt(amount)) setOpen(true) } return ( {(!open && !status) && <> <Card> 套餐到期时间：{new Date(expiration * 1000).toLocaleString()} navigate('/')}>返回首页 </Card> <Card> <Form.Item label="邮箱" name="email"><Input /></Form.Item> <Form.Item label="数量" name="amount"><InputNumber style={{ width: "100%" }} addonAfter={"年"} min={1} max={100} /></Form.Item> <Form.Item> 支付 </Form.Item> </Card> </>} {(open && !status) && <CheckoutForm email={email} amount={amount} />} {status && <Complete customerEmail={customerEmail} />} ) } export default Payment测试时可以使用测试卡号完成支付：4242424242424242，日期任意一个未来日期即可，CVC为任意三位数三、履行订单在用户支付完成后，我们需要履行订单，比如给用户发货、充值卡点、续费套餐等等。stripe推荐使用Webhook HTTP端点来接收事件，也可以使用事件接口轮询。下面的示例中，处理结账会话支付完成事件，给用户的VIP套餐续期。import ( "encoding/json" "log" "net/http" "strconv" "time" "server/model" "github.com/gin-gonic/gin" "github.com/stripe/stripe-go/v78" "github.com/stripe/stripe-go/v78/checkout/session" "github.com/stripe/stripe-go/v78/event" ) func init() { stripe.Key = "sk_test_51P12rrI3yx14f44QTHOPqliw9So8GBpZ6EIPi7v1iDbdlgr5NH0vXIH3zUjQJwxLI7pbAsfSErQOy75sAVr7q4ra00AODgXKD0" go func() { d := 5 * time.Second for { time.Sleep(d) events := event.List(&stripe.EventListParams{ CreatedRange: &stripe.RangeQueryParams{ GreaterThanOrEqual: time.Now().Add(-d).Unix(), }, Type: stripe.String(string(stripe.EventTypeCheckoutSessionCompleted)), }) for events.Next() { event := events.Event() if event.Type == stripe.EventTypeCheckoutSessionCompleted { var session stripe.CheckoutSession json.Unmarshal(event.Data.Raw, &session) id, email, amountStr := session.ID, session.Metadata["email"], session.Metadata["amount"] amount, _ := strconv.Atoi(amountStr) log.Println("order completed:", id, email, amount) expiration, err := model.SelectUserExpiration(email) if err == nil { now := time.Now().Unix() if expiration == nil || *expiration < now { expiration = &now } *expiration = time.Unix(*expiration, 0).AddDate(amount, 0, 0).Unix() model.UpdateUserExpiration(email, *expiration) } else { log.Println("Select Expiration Error:", err) } } } } }() }
路由Gin路由遵循Restful API接口规范，是基于httproute实现的基本路由语法如下，httpMethod为请求类型，relativePath为路径，handlers为该路由响应函数列表（多个中间件加一个执行函数）router.<httpMethod>(relativePath string, handlers ...HandlerFunc)支持GET、POST、PUT、PATCH、DELETE、OPTIONS等请求类型，Any方法匹配所有类型，Match方法匹配指定的多个类型func main() { router := gin.Default() router.GET("/login", loginEndpoint) router.POST("/login", loginEndpoint) router.Any("/login", loginEndpoint) router.MatchMatch([]string{"POST","GET"}, "/login", loginEndpoint) router.Run(":8080") }路径规则如下"/path"，指定路径，只能匹配/path"/:path"，参数路由，可以匹配/a和/b等，不能匹配/a/b"/*path"，任意路由，可以匹配/、/a和/a/b等所有路由路由按指定路径优先进行匹配，参数路由和任意路由在同一级不可共存func main() { router := gin.Default() // 匹配/user/john但不会匹配/user/或者/user router.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) // 将匹配/user/john/和/user/john/send // 如果没有其他路由匹配 /user/john，它将重定向到 /user/john/ router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") message := name + " is " + action c.String(http.StatusOK, message) }) router.Run(":8080") }路由分组func main() { router := gin.Default() // 简单的路由组: v1 v1 := router.Group("/v1") { v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) } // 简单的路由组: v2 v2 := router.Group("/v2") { v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) } router.Run(":8080") }路由中间件Gin框架是洋葱结构的，可以同时使用多个中间件，按注册顺序执行，中间件中使用c.Next()函数调用下一个中间件或路由响应函数，如果没有使用c.Next()则执行完此中间件将会返回func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // 请求前 c.Next() // 请求后 latency := time.Since(t) log.Print(latency) } } func main() { r := gin.New() r.Use(Logger()) r.GET("/test", func(c *gin.Context) { }) r.Run(":8080") }中间件可以给指定路由用，比如r.GET("/test", Logger, loginEndpoint)代理拼接真实服务器链接，使用http请求资源，再使用DataFromReader方法返回资源const apiAddr = "https://example.com" func Proxy(c *gin.Context) { url, err := url.Parse(apiAddr + c.Request.URL.Path) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "err": err.Error(), }) } resp, err := http.DefaultClient.Do(&http.Request{ URL: url, Method: c.Request.Method, Body: c.Request.Body, Header: c.Request.Header, }) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "err": err.Error(), }) } defer resp.Body.Close() c.DataFromReader(resp.StatusCode, resp.ContentLength, resp.Header.Get("Content-Type"), resp.Body, nil) }使用方法，注意：使用Any匹配所有HTTP类型的请求，使用/*path匹配所有路径，可加入鉴权中间件限制代理的访问router.Any("/:api/*path", Proxy)
简化HTTP服务器的配置和管理，使用系统自带的服务，无需安装任何东西，将指定目录作为HTTP服务器的根目录，一键开启静态服务器实现原理Window10和Window11自带PowerShell，使用HttpListener类创建HTTP服务，绑定端口接收请求并返回指定文件。Linux系统自带python，用http.server模块即可创建简易的HTTP服务器。Windows系统实现创建server.ps1文件，并写入如下内容# PowerShell Web Server Param( [parameter(Mandatory=$false)][Int]$port=8080, [parameter(Mandatory=$false)][String]$webPath="www" ) Add-Type -AssemblyName System.Web $scriptPath = $PSScriptRoot $http = [System.Net.HttpListener]::new() $http.Prefixes.Add("http://localhost:$port/") $http.Start() function ConvertTo-Base64($str){ $result = [Convert]::ToBase64String([System.Text.UTF8Encoding]::UTF8.GetBytes($str)) return $result } function ConvertFrom-Base64($str){ $byteArray = [Convert]::FromBase64String($str) return [System.Text.UTF8Encoding]::UTF8.GetString($byteArray) } function Send-WebResponse($context, $content) { if($content.GetType().Name -ne "String"){ $content = ConvertTo-JSON $content } $buffer = [System.Text.Encoding]::UTF8.GetBytes($content) $context.Response.ContentLength64 = $buffer.Length $context.Response.OutputStream.Write($buffer, 0, $buffer.Length) } function Get-PostData{ $reader = new-object System.IO.StreamReader($context.Request.InputStream) $text = $reader.ReadToEnd() # $text = $text.Replace("%3D","=") # $text = ConvertFrom-Base64 $text $text = [System.Web.HttpUtility]::UrlDecode($text) Write-Host "$text" return ConvertFrom-Json $text } if ($http.IsListening) { write-host "HTTP Server Ready! " -f 'black' -b 'gre' write-host "$($http.Prefixes)" -f 'y' } # INFINTE LOOP, Used to listen for requests while ($http.IsListening) { $context = $http.GetContext() $RequestUrl = $context.Request.Url.LocalPath Write-Host "$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) : $($context.Request.Url)" -f 'mag' # Get Request Url if ($context.Request.HttpMethod -eq 'GET') { # Redirect root to index.html if($RequestUrl -eq "/") { $RequestUrl = "/index.html" } if(Test-Path "$scriptPath\$webPath\$RequestUrl"){ $fileStream = [System.IO.File]::OpenRead( "$scriptPath\$webPath\$RequestUrl" ) $fileStream.CopyTo( $Context.Response.OutputStream ) $fileStream.Close() } else{ Send-WebResponse $context "404 : Not found $RequestUrl" } $context.Response.Close() } }脚本接收两个参数，第一个是绑定端口，默认为8080，第二个是指定根目录，默认为当前目录下的www\目录。打开PowerShell窗口，执行.\server.ps1，也可以右键菜单使用PowerShell执行此脚本，出现下图窗口表示执行成功，关闭窗口就可以关闭HTTP服务器。如果遇到执行异常，可以管理员身份打开PowerShell，运行命令Set-ExecutionPolicy RemoteSigned解锁ps1脚本执行权限（系统默认没有开启）。为了方便，可以创建如下内容的的start.bat文件，一键开启静态服务器，无需管理员身份，也没有权限问题。if "%1" neq "" (set port=%1) else (set port=8080) if "%2" neq "" (set root=%2) else (set root=www) start http://localhost:%port% powershell -ExecutionPolicy RemoteSigned .\server.ps1 %port% %root%Linux系统实现相比Windows系统简单许多，Linux系统默认已经安装python，使用http.server模块，一条命令可启动。绑定本地网卡地址，监听8080端口，使用www作为根目录，开启HTTP服务器的命令如下。python -m http.server 8080 -b localhost -d www可选参数说明-b：指定绑定网卡地址，默认为0.0.0.0，不安全，可更改为localhost-d：指定根目录，默认为当前目录为了方便，可以创建如下内容的的start.sh文件，一键开启静态服务器。#!/bin/bash xdg-open http://localhost:8080 python -m http.server 8080 -b localhost -d www
comfyui-api文档GET /historyGET /history获取所有历史任务数据请求参数名称位置类型必选说明prompt_idquerystring否8b918008-751f-414c-9575-7174e841ceac返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /embeddingsGET /embeddings获取一个列表返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /history/{prompt_id}GET /history/8b918008-751f-414c-9575-7174e841ceac获取历史任务数据(根据任务id获取历史数据)返回示例成功返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /extensionsGET /extensions获取扩展节点文件列表返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构POST /upload/imagePOST /upload/image上传图片接口Body 请求参数image: string 请求参数名称位置类型必选说明bodybodyobject否none» imagebodystring(binary)是图片将以二进制格式发送到服务器返回示例成功{ "name": "aaa (7).webp", "subfolder": "", "type": "input" }返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构POST /upload/maskPOST /upload/mask上传蒙版图片接口，一般用于局部重绘Body 请求参数image: string type: input subfolder: clipspace original_ref: "{“filename”:”下载.png”,”type”:”input”,”subfolder”:”clipspace”}" 请求参数名称位置类型必选说明bodybodyobject否none» imagebodystring(binary)是图片将以二进制格式发送到服务器» typebodystring否上传图片的目标文件夹» subfolderbodystring否上传图片的目标子文件夹» original_refbodystring是无返回示例成功{ "name": "下载.png", "subfolder": "clipspace", "type": "input" }返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /viewGET /view图片的在线预览接口（上传图像，生图图像，蒙蔽图像，均通过该接口预览）请求参数名称位置类型必选说明filenamequerystring是图片名称typequerystring否图片存放位置的文件夹（input为长传图片，output为生成的图片）subfolderquerystring否子文件夹(没有可不填)previewquerystring否预览channelquerystring否无返回示例成功" "返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构POST /view_metadata/{folder_name}POST /view_metadata/{folder_name}无请求参数名称位置类型必选说明folder_namepathstring是none返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /system_statsGET /system_stats系统统计信息接口返回示例成功{ "system": { "os": "posix", "python_version": "3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]", "embedded_python": false }, "devices": [ { "name": "cuda:0 NVIDIA GeForce RTX 2080 Ti : cudaMallocAsync", "type": "cuda", "index": 0, "vram_total": 23266590720, "vram_free": 13600655680, "torch_vram_total": 6811549696, "torch_vram_free": 26970432 } ] }返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /promptGET /prompt获取服务器当前剩余任务列队的数量返回示例成功{ "exec_info": { "queue_remaining": 1 } }返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构POST /promptPOST /prompt绘图任务的下发接口，此接口只做任务下发，返回任务ID信息。此接口只做任务下发，返回任务ID信息。Body 请求参数{ "client_id": "533ef3a3-39c0-4e39-9ced-37d290f371f8", "prompt": { "3": { "inputs": { "seed": 764714814161513, "steps": 26, "cfg": 5, "sampler_name": "dpmpp_3m_sde_gpu", "scheduler": "karras", "denoise": 1, "model": [ "40", 0 ], "positive": [ "49", 0 ], "negative": [ "6", 0 ], "latent_image": [ "5", 0 ] }, "class_type": "KSampler" }, "5": { "inputs": { "width": 1024, "height": 768, "batch_size": 1 }, "class_type": "EmptyLatentImage" }, "6": { "inputs": { "text": "", "clip": [ "40", 1 ] }, "class_type": "CLIPTextEncode" }, "8": { "inputs": { "samples": [ "3", 0 ], "vae": [ "40", 2 ] }, "class_type": "VAEDecode" }, "9": { "inputs": { "filename_prefix": "ComfyUI", "images": [ "8", 0 ] }, "class_type": "SaveImage" }, "13": { "inputs": { "clip_vision": [ "39", 0 ], "image": [ "34", 0 ] }, "class_type": "CLIPVisionEncode" }, "19": { "inputs": { "strength": 1, "noise_augmentation": 0, "conditioning": [ "42", 0 ], "clip_vision_output": [ "13", 0 ] }, "class_type": "unCLIPConditioning" }, "34": { "inputs": { "image": "clipspace/clipspace-mask-1645940.7000000002.png [input]", "choose file to upload": "image" }, "class_type": "LoadImage" }, "36": { "inputs": { "clip_vision": [ "39", 0 ], "image": [ "38", 0 ] }, "class_type": "CLIPVisionEncode" }, "37": { "inputs": { "strength": 0.75, "noise_augmentation": 0, "conditioning": [ "19", 0 ], "clip_vision_output": [ "36", 0 ] }, "class_type": "unCLIPConditioning" }, "38": { "inputs": { "image": "beijing1 (2).webp", "choose file to upload": "image" }, "class_type": "LoadImage" }, "39": { "inputs": { "clip_name": "clip_vision_g.safetensors" }, "class_type": "CLIPVisionLoader" }, "40": { "inputs": { "ckpt_name": "sd_xl_base_1.0.safetensors" }, "class_type": "CheckpointLoaderSimple" }, "42": { "inputs": { "conditioning": [ "6", 0 ] }, "class_type": "ConditioningZeroOut" }, "43": { "inputs": { "safe": "enable" }, "class_type": "HEDPreprocessor" }, "44": { "inputs": { "safe": "enable", "image": [ "34", 0 ] }, "class_type": "HEDPreprocessor" }, "45": { "inputs": { "images": [ "44", 0 ] }, "class_type": "PreviewImage" }, "46": { "inputs": { "control_net_name": "control-lora-depth-rank256.safetensors" }, "class_type": "ControlNetLoader" }, "47": { "inputs": { "image": [ "34", 0 ] }, "class_type": "ScribblePreprocessor" }, "48": { "inputs": { "images": [ "47", 0 ] }, "class_type": "PreviewImage" }, "49": { "inputs": { "strength": 0.5, "conditioning": [ "37", 0 ], "control_net": [ "46", 0 ], "image": [ "47", 0 ] }, "class_type": "ControlNetApply" } } }请求参数名称位置类型必选说明bodybodyobject否none返回示例成功{ "prompt_id": "352c1fc4-7382-4c4a-965f-583c4b126a1b", "number": 38, "node_errors": {} }返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /object_infoGET /object_info获取系统中所有组件以及可用参数返回示例成功返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /object_info/{node_class}GET /object_info/KSampler根据组件名称获取系统中组件参数返回示例成功返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /queueGET /queue获取详细任务队列信息，正在运行的以及挂起的返回示例成功返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构POST /queuePOST /queue删除列队/无返回信息则为成功Body 请求参数{ "delete": "string" }请求参数名称位置类型必选说明bodybodyobject否none» deletebodystring是包含任务id的列表返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构GET /interruptGET /interrupt取消当前任务/不需任何参数返回示例200 Response{}返回结果状态码状态码含义说明数据模型200OK成功Inline返回数据结构数据模型
简介基于stable diffusion的功能强大、模块化程度高的AI绘图软件，自带图形界面和接口服务的后端节点/图形/流程图界面用于实验和创建复杂的稳定扩散工作流程，无需编写任何代码完全支持SD1.x、SD2.x、SDXL和SVD异步队列系统许多优化：仅重新执行工作流程中在执行之间发生变化的部分命令行选项：--lowvram使其在显存小于3GB的GPU上运行（在显存较低的GPU上自动启用）即使您没有 GPU，也可以使用：--cpu（慢）从生成的PNG文件加载完整的工作流程（带有种子）将工作流程保存/加载为Json文件启动速度非常快完全离线工作：永远不会下载任何东西安装即使没有显卡也可以使用处理器运行comfyui，但是不推荐这样做（非常慢），最好拥有超过32GB内存和6GB显存英伟达显卡。Window系统推荐使用绘世启动器傻瓜式安装，不用浪费时间折腾各种依赖的安装。安装最新的显卡驱动下载绘世启动器，解压到一个空目录下启动绘世，按照提示操作需要先安装.NET桌面运行环境，点击确认和下载并安装软件后再次启动绘世等待软件自动下载好数据文件后，按提示进入到内核下载按照下图选择下载内核，这里可以选择不安装torch，可以在后面再安装安装完成软件会自动关闭，再次启动软件即可到软件操作界面接下来可安装模型、插件，再一键启动comfyuilinux系统安装python，不支持python3.12版本，推荐3.11版本安装torch，打开pytorch官网，往下翻找到并选择安装命令，如下图所示如果是英伟达显卡并安装最新版本驱动可执行此命令：pip install torch torchvision torchaudio克隆comfyui仓库（先安装git），git clone https://github.com/comfyanonymous/ComfyUI.git切换到comfyui目录：cd ComfyUI，安装comfyui依赖：pip install -r requirements.txt启动comfyui：python main.py使用下载checkpoints大模型文件，比如sd_xl_base_1.0.safetensors可从此网站下载：https://hf-mirror.com/stabilityai/stable-diffusion-xl-base-1.0/tree/main将文件移动到comfyui的models\checkpoints目录中启动comfyui，在浏览器中打开链接：http://127.0.0.1:8188选择默认工作流，点击右侧的load default在load checkpoints节点中选择刚才下载的模型可修改输出图片尺寸和随机数种子等等参数，点击右侧的queue prompt即可生成图片(速度取决于所选模型和电脑配置)建议使用翻墙环境，否则下载插件和模型等等会有各种网络问题，可使用模型镜像站缓解此类问题插件安装（中文插件）安装comfyui管理器插件，可使用绘世的插件安装功能，也可手动克隆ComfyUI-Manager到ComfyUI/custom_nodes目录启动comfyui，在浏览器上右侧可看到manager按钮表示安装成功，点击可使用其各种功能安装中文插件，使用manager插件的install custom nodes功能搜索language从结果列表中选择AIGODLIKE-COMFYUI-TRANSLATION插件安装安装完成后重启，点击右侧switch locale即可切换中文参考链接项目地址：https://github.com/comfyanonymous/ComfyUI工作流示例：https://comfyanonymous.github.io/ComfyUI_examples管理器插件：https://github.com/ltdrdata/ComfyUI-Manager国内镜像站：https://hf-mirror.com国内模型站：https://aigccafe.net
安装$ sudo apt install git #两个都安装命令git init # 初始化本地git仓库（创建新仓库） git config --global user.name "xxx" # 配置用户名 git config --global user.email "xxx@xxx.com" # 配置邮件 git config --global color.ui true # git status等命令自动着色 git config --global color.status auto git config --global color.diff auto git config --global color.branch auto git config --global color.interactive auto git config --global --unset http.proxy # remove proxy configuration on git git clone git+ssh://git@192.168.53.168/VT.git # clone远程仓库 git status # 查看当前版本状态（是否修改） git add xyz # 添加xyz文件至index git add . # 增加当前子目录下所有更改过的文件至index git commit -m 'xxx' # 提交 git commit --amend -m 'xxx' # 合并上一次提交（用于反复修改） git commit -am 'xxx' # 将add和commit合为一步 git rm xxx # 删除index中的文件 git rm -r * # 递归删除 git log # 显示提交日志 git log -1 # 显示1行日志 -n为n行 git log -5 git log --stat # 显示提交日志及相关变动文件 git log -p -m git show dfb02e6e4f2f7b573337763e5c0013802e392818 # 显示某个提交的详细内容 git show dfb02 # 可只用commitid的前几位 git show HEAD # 显示HEAD提交日志 git show HEAD^ # 显示HEAD的上一个版本的提交日志 ^^为上两个版本 ^5为上5个版本 git tag # 显示已存在的tag git tag -a v2.0 -m 'xxx' # 增加v2.0的tag git show v2.0 # 显示v2.0的日志及详细内容 git log v2.0 # 显示v2.0的日志 git diff # 显示所有未添加至index的变更 git diff --cached # 显示所有已添加index但还未commit的变更 git diff HEAD^ # 比较与上一个版本的差异 git diff HEAD -- ./lib # 比较与HEAD版本lib目录的差异 git diff origin/master..master # 比较远程分支master上有本地分支master上没有的 git diff origin/master..master --stat # 只显示差异的文件，不显示具体内容 git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义（用于push/pull/fetch） git branch # 显示本地分支 git branch --contains 50089 # 显示包含提交50089的分支 git branch -a # 显示所有分支 git branch -r # 显示所有原创分支 git branch --merged # 显示所有已合并到当前分支的分支 git branch --no-merged # 显示所有未合并到当前分支的分支 git branch -m master master_copy # 本地分支改名 git checkout -b master_copy # 从当前分支创建新分支master_copy并检出 git checkout -b master master_copy # 上面的完整版 git checkout features/performance # 检出已存在的features/performance分支 git checkout --track hotfixes/BJVEP933 # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支 git checkout v2.0 # 检出版本v2.0 git checkout -b devel origin/develop # 从远程分支develop创建新本地分支devel并检出 git checkout -- README # 检出head版本的README文件（可用于修改错误回退） git merge origin/master # 合并远程master分支至当前分支 git cherry-pick ff44785404a8e # 合并提交ff44785404a8e的修改 git push origin master # 将当前分支push到远程master分支 git push origin :hotfixes/BJVEP933 # 删除远程仓库的hotfixes/BJVEP933分支 git push --tags # 把所有tag推送到远程仓库 git fetch # 获取所有远程分支（不更新本地分支，另需merge） git fetch --prune # 获取所有原创分支并清除服务器上已删掉的分支 git pull origin master # 获取远程分支master并merge到当前分支 git mv README README2 # 重命名文件README为README2 git reset --hard HEAD # 将当前版本重置为HEAD（通常用于merge失败回退） git rebase git branch -d hotfixes/BJVEP933 # 删除分支hotfixes/BJVEP933（本分支修改已合并到其他分支） git branch -D hotfixes/BJVEP933 # 强制删除分支hotfixes/BJVEP933 git ls-files # 列出git index包含的文件 git show-branch # 图示当前分支历史 git show-branch --all # 图示所有分支历史 git whatchanged # 显示提交历史对应的文件修改 git revert dfb02e6e4f2f7b573337763e5c0013802e392818 # 撤销提交 git ls-tree HEAD # 内部命令：显示某个git对象 git rev-parse v2.0 # 内部命令：显示某个ref对于的SHA1 HASH git reflog # 显示所有提交，包括孤立节点 git show HEAD@{5} git show master@{yesterday} # 显示master分支昨天的状态 git log --pretty=format:'%h %s' --graph # 图示提交日志 git show HEAD~3 git show -s --pretty=raw 2be7fcb476 git stash # 暂存当前修改，将所有至为HEAD状态 git stash list # 查看所有暂存 git stash show -p stash@{0} # 参考第一次暂存 git stash apply stash@{0} # 应用第一次暂存 git grep "delete from" # 文件中搜索文本“delete from” git grep -e '#define' --and -e SORT_DIRENT git gc git fsck
一、什么是JWTJSON Web Token (JWT)是一个开放标准(RFC 7519)，它定义了一种紧凑的、自包含的方式，用于作为JSON对象在各方之间安全地传输信息。特别适用于分布式站点的单点登录（SSO）场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息，以便于从资源服务器获取资源，也可以增加一些额外的其它业务逻辑所必须的声明信息，该token也可直接被用于认证，也可被加密。二、JWT的数据结构实际的JWT由三部分组成，如下所示eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ewoJJ2lkJzonMTAnLAoJJ2V4cCc6JzIzMDE1OTc0MzMnCn0=.05dd35b4d20c95430cd1b63406f861de7e4c1476f9dbffa25f30fe08baf8f530中间用点（.）分隔成三个部分。JWT的三个部分依次如下：Header（头部）Payload（负载）Signature（签名）HeaderHeader部分是一个JSON对象，描述JWT的元数据，通常是下面的样子{ "alg": "HS256", "typ": "JWT" }上面代码中，alg属性表示签名的算法（algorithm），默认是HMAC SHA256（写成 HS256）；typ属性表示这个令牌（token）的类型（type），JWT令牌统一写为JWT。将上面的JSON对象使用Base64URL算法（详见后文）转成字符串就成了第一部分Header。PayloadPayload部分也是一个JSON对象，用来存放实际需要传递的数据。JWT规定了7个官方字段，供选用。iss (issuer)：签发人exp (expiration time)：过期时间sub (subject)：主题aud (audience)：受众nbf (Not Before)：生效时间iat (Issued At)：签发时间jti (JWT ID)：编号我们还可以在这个部分自己定义字段，下面就是一个例子{ "sub": "1234567890", "name": "John Doe", "admin": true }这个JSON对象也要使用Base64URL算法转成字符串。SignatureSignature部分是对前两部分的签名，防止数据篡改。首先，需要指定一个密钥（secret）。这个密钥只有服务器才知道，不能泄露给用户。然后，使用Header里面指定的签名算法（默认是HMAC SHA256），按照下面的公式产生签名。HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)Base64URL前面提到，Header和Payload串型化的算法是Base64URL。这个算法跟Base64算法基本类似，但有一些小的不同。JWT作为一个令牌（token），有些场合可能会放到URL（比如api.example.com/?token=xxx）。Base64有三个字符+，/和=，在 URL里面有特殊含义，所以要被替换掉：去除尾部的=、+替换成-，/替换成_ 。这就是Base64URL算法。算出签名以后，把Header、Payload、Signature三个部分拼成一个字符串，每个部分之间用"点"（.）分隔，就可以返回给用户。三、特点JWT默认是不加密，但也是可以加密的。生成原始Token以后，可以用密钥再加密一次。JWT不加密的情况下，不能将秘密数据写入JWT。JWT不仅可以用于认证，也可以用于交换信息。有效使用JWT，可以降低服务器查询数据库的次数。JWT的最大缺点是，由于服务器不保存session状态，因此无法在使用过程中废止某个token，或者更改token的权限。也就是说，一旦JWT签发了，在到期之前就会始终有效，除非服务器部署额外的逻辑。JWT本身包含了认证信息，一旦泄露，任何人都可以获得该令牌的所有权限。为了减少盗用，JWT的有效期应该设置得比较短。对于一些比较重要的权限，使用时应该再次对用户进行认证。为了减少盗用，JWT不应该使用HTTP协议明码传输，要使用HTTPS协议传输。四、使用安装go install "github.com/golang-jwt/jwt/v4"定义claims和serecttype UserClaim struct { UserName string `json:"user_name"` jwt.RegisteredClaims // 注意!这是jwt-go的v4版本新增的，原先是jwt.StandardClaims } var Secret = []byte("abggfdfsdgef") // 定义secret生成Tokenfunc GenToken(user string) (string, error) { claim := UserClaim{ user, jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), //24h后过期 Issuer: "gameFei", //签发人 }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) //使用HS256算法 return token.SignedString(conf.Secret) }解析Tokenfunc ParseToken(tokenStr string) (*UserClaim, error) { token, err := jwt.ParseWithClaims(tokenStr, &UserClaim{}, func(token *jwt.Token) (interface{}, error) { return Secret, nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*UserClaim); ok && token.Valid { return claims, nil } return nil, fmt.Errorf("invalid token") }
长的丑的才叫妖怪，漂亮的叫妖精生前何必久睡，死后自会长眠老子来到这个世上就没打算活着回去天下风云出我辈，一入江湖岁月催，皇图霸业谈笑中，不胜人生一场醉人生有两条路，一条用心走，叫做梦想，一条用脚走，叫做现实总有一天，你会遇到这样一个人，她温柔善良，漂亮大方，她不要你的钱，不要你的车，不要你的房，也不要你你若安好，那还得了假如生活欺骗了你，不要灰心，因为明天也一样钱就是空气，没有它活不下去一生不轻爱，一爱爱一生我谈过最长的恋爱，就是自恋，我爱自己，没有情敌你如果认识从前的我，也许你会原谅现在的我物忌全胜，事忌全美，人忌全盛心小了，所有的小事就大了；心大了，所有的大事都小了人生不过如此，且行且珍惜。自己永远是自己的主角，不要总在别人的戏剧里充当着配角得到的往往比失去的更重要，它是人生的价值与意义失去的东西，其实从来未曾真正地属于你，也不必惋惜即以背影倾天下，何必转身乱芳华
定义工作本身并不能带了经济上的安全感，具备良好的思考、学习、创造与适应能力，才能立于不败之地。拥有财富，并不代表经济独立，拥有创造财富的能力才真正可靠。所以，自我提升和完善尤为重要。它包括四个层面，身体（锻炼、营养、压力调节）、精神（实现价值、忠诚学习、冥想）、智力（阅读、想象、规划、写作）、社会/情感（服务、移情、统合综效、内在安全感）。身体层面，是指有效呵护我们的身体—健康饮食，充足的休息以及定期锻炼。它是属于第二象限事物，重要但不紧急，因此很少人能坚持不懈。精神层面，精神为我们指引人生方向，是人的本质、核心和对价值体系的坚持，是生活中非常私人又至观重要的领域。它能够调动人体内具有激励和鼓舞作用的资源，把你同所有人类的永恒真理紧紧联系在一起。如果我们能够用心把握生活的方向和生命的真谛，就如同得到了一把遮风挡雨的打伞，源源不断地使我们活得新的力量。如果能够持之以恒，效果会更加明显。智力层面，它只要靠教育、不断学习新知识，磨练心智，开阔视野。可以通过看书、写作等完成。社会/情感层面。在改变他人上，我们可以选择清晰而真实地反映出他人的形象，肯定她们的积极性和责任心，帮助她们改变行为模式，成为讲原则、懂判断、独立自主、有价值的个体。反映他人的正面形象并不会贬低我们自己，反而会使我们更加强大。我们如果有足够的财富对别人做情感投资，我们越擅长发掘别人的潜力，就越能在配偶、子女、同事或者雇员身上发挥自己的想象力，而不是记忆力。我们不应该给她们贴标签，与她们共处是要从全新的角度大量她们，帮助我们独立和实现自我，并建立起美满、丰富和卓有成效的人际关系。对于我们来说，这四个层面都要去更新，因为只有平衡好更新进度，才能取得最理性的效果，忽视任何一个层面都会对其它层面产生消极的影响。在更新的过程中，我们不要贪多，而是一点一点去攻破它，应用我们学习到的单点突破法，计划实施总结评估，立真把它们平衡。管理自我主要做好三样：时间管理、精力管理、情绪管理时间管理。改变自己从认识自己开始，管理时间也是从认识时间开始。一开始，最好的方法就是记录时间，把每天花费的时间记录下来。就像理财一样，先记录钱花在哪里，才知道哪些钱不该花，或者少花。第一阶段，可以这样简单记录，看看大块时间用在哪里。开会（0.5）吃饭（1.0）写作（2.0）刷抖音（3.0）写代码（4.0）你看，明显是刷抖音时间长，这个可以尽量减少或避免。第二阶段，记录具体时间和具体事件。6:00 ~ 7:00 晨练7:00 ~ 8:00 读书写作8:00 ~ 9:00 去上班9:00 ~ 11:00 工作12:00 ~ 14:00 午休14:00 ~ 16:00 梳理积分商城16:00 ~ 16:30 休息16:30 ~ 19:00 综合分析竞品商场19:30 ~ 整理主要目的，看看哪个时间做了哪些事，具体花费多少时间，后边有大用。第三阶段，清晨安排三件事很多人不喜欢记录，太麻烦，记来记去也没什么改变。任何工具如果使用起来太复杂，就会有90%的人放弃。清晨三件事，非常简单，也是我一直在用的。每天早晨起来，就按照重要程度，把当天最需要干完的三件事完成。我今天最需要做完的三件事：1.工作报告整理，各个项目组进度把控，协调资源。2.跟客户谈具体合作事项。3.公众号文章一篇，知乎问答一篇。工作时间，把前两项搞定，业余时间把最后一项搞定。如果还剩下时间，我就会做一些长期规划的事，明天、后天，以及未来一个月需要准备做的事。做好以上这些，就能极大提升工作效率吗，不能。你一定遇到过这种情况，时间很充足，坐在那里，啥也干不进去。比如，一下午过去了，报告一个字没有写，有时间，没状态。遇到这种情况，不是时间出了问题，而是你的精力出了问题。精力管理。有一本书《精力管理》，讲了精力来源的四个方面：体能、思维、情感、意志。我跟进自己的实践和观察，体能和思维对我影响很大。体能也是精力最基本的来源。我记得上高中那会，早上为了节省饭钱，只吃馒头和方便面。那几天上早自习感觉特别累，总爱打瞌睡。后来，吃了两个星期实在扛不住了，又去食堂吃饭了。那时候感觉特别饿，没精神，没有想过饮食这么重要。刚刚工作那会也是，早上不吃饭，死磕一上午，中午吃得又多犯困。整个人一天的工作状态不是特别好，还好那会年轻，用精神力扛着。现在作息规律，早上跑步、读书，吃早饭。上午一般脑子清楚，一般做一些分析类工作。午餐之后，会溜达一会，消消食，然后午睡。下午脑子不那么灵光，有一些疲倦，会做一些总结和整理工作。晚上吃完饭，也有一个不错的状态，会做一些有创意的工作。一句话，你一定要在状态最好的时候，做最难最复杂的事。注意自己的精神状态，太疲惫了就休息一会，磨刀不误砍柴工。死磕的状态不能持续，要养成固定的习惯。情绪管理。做到以上两点，已经可以称为高手了。能够利用自己的情绪，高效率完成工作更是高手中的高手。有时间、精力充沛，但还是遇到一些工作，干不下去。人人有七情六欲，当你发现自己不喜欢一项工作的时候，怎么都干不下去。这时候心里一直抵触，然后开始怀疑自己，是不是不适合做这个；欺骗自己，明天做也没关系。如果这件事对你很重要，就一定要逼自己做下去。有一个小技巧：给自己1个小时的放空时间，在这个时间不带着情绪做事，不考虑要做的事有没有意义，有没有帮助，能够得到什么结果。把情绪放在做事上，不放在心里，在这一刻你可以把自己暂时当作没有情感的机器。先不要急着否定我，你可以去试试，如果真的不好用，你可以来找我。我最开始写作的时候，最多能写500字，我看别人的文章动辄几千，心态直接就不好了。坐在椅子上，一直在想1000字太长了，根本写不出来，完不成。没有把情绪用在内容上，而是用在否定自己上，情绪低落，心态消极。后来，有一天我试着放下情绪，不考虑自己写的好不好，语句通顺不通顺，逻辑严不严谨，都不重要。哪怕自己写的是垃圾也好，专心用在写字上，那一刻好像忘记了身边发生的事，忘记了这个世界。实用方法1、正确看待时间理解拖延症。心理学家发现，我们看待时间有两种角度，“近期未来”和“远期未来”。大家在这两种角度上看待问题会有不同的答案近期未来视角下，人们更容易在具体的情景下考虑问题，想得更多的不是“要不要做”而是“怎么去做”。比如，如何通过最近的考试，如何完成老板安排的任务。远期未来视角下，人们更多的是考虑这件事对自己的价值和意义。比如考上MBA，开始自己创业。近期未来——远期未来的框架可以用来分析生活的方方面面，生活中的很多问题，都是由近期未来和远期未来的不一致导致的。产生拖延症的一个原因就是，远期未来中有一个抽象且意义重大的目标，遇上了实际的阻碍和便利的替代活动。比如，想要考上MBA，阻碍是学习效果不理想，便利的替代活动是看电影和打游戏，借此逃避阻碍。在这种状况下，人们通常的应对方式是不断强化远期目标的意义，“考上MBA是我理想的第一步”“如果没考上还能去干嘛”，但这不仅无法解决问题，还可能因为加大了近期未来和远期未来的冲突使问题更严重。想要处理好近期未来和远期未来，我们可以采用下面两个策略：1.不要过分强调远期未来的意义，多考虑具体执行方式。2.增加逃避的难度，比如删掉游戏，关掉手机。把事件花在值得的事上。除了拖延症，在使用时间上，我们还会发现一个奇怪的现象，每天的日程排得满满的，看起来似乎做了很多事，但是感觉却没有什么积累，像是什么都没做一样。这可能是因为你做的事，很多都是短半衰期的，而长半衰期的事做的太少。（半衰期最早是物理学概念，这里就是所做的事带来的收益随时间衰减的速度）比如玩游戏或短视频，在做这件事时带来的快乐是很强的，但是一旦停止，快乐会很快流失，这就是短半衰期的事；而每天读一本书，背几个单词，虽然当时没有感到什么收益，但是你掌握的知识在未来很长时间内都有可能给你回报，这就是长半衰期的事。在分析一件事值不值得做，花多少精力做的时候，可以从两个角度评估：一是这件事在当下给我带来收益的大小，这个收益可以是心智、感情、身体、物质等，可以称之为【收益值】；二是这项收益随时间衰减的速度，可以称为【收益半衰期】，半衰期长的事件，其影响会持续较久。想要自我提升，就必须多做长半衰期的事，而少做短半衰期的事。否则你会发现，自己是在重复的抓起沙子，刚获得一点快感，手掌中紧握的沙子便和时间一起飞速溜走。1.收益值的高低无关紧要，只要不是短半衰期的事情，只要收益可累加，就尽管去做。2.不要只盯着那些高大上的事情，一些不重要、不紧急的事情，只要对你有长期的益处，仍然可以去做。2、做出更好的选择一件事对不同的人来说，收益也是不同的，那么我们该如何做出对自己来说收益值更高，半衰期更长的事情呢？那就是找到更高的目标，从而做出更好的选择。决策心理学认为，人在面对选择时，通常会采用【满意原则】，而不是【最优原则】。就是说人会从自己最熟悉的选项开始一个接一个考察，当找到一个满足内心标准的选项，就会采纳这个选项，余下的选项就被废弃了，而不是对比所有选项，找到其中最好的。因此，一个人能不能做出更好的选择，与他采纳的标准有很大关系。如果内心标准很低，很快便能找到满足标准的选项，而这个选择很可能在所有选项中并不优越；如果内心标准较高，那么就会比较更多的选项，从中找到更好的选项。如果你知道自己的最高目标是什么，自然是最好不过了。如果现阶段，你确实不知道自己的最高目标，那么你可以尝试一个简单方法，回想一下最近的一周，你做过的最有意义的一件事是什么，也可以是两周、一个月或者半年，你做过的最有意义的事。然后再回忆一下，你在做这件事的过程中以及完成后的内心感受，这件事为什么对你如此重要，是因为让你感到难得的愉悦，内心的满足，或是难以抑制的成就感？这些最有意义的事带来的感受，很可能是你内心深处所追求的东西，借此你可以思考，以什么作为人生最高目标，可以让你长期，持续获得这种感觉。不过现实会限制我们做选择的范围，其中最大的阻碍不是没钱，没能力，而是隐含假设。其中比较典型的是下面三个：第一个【赛道假设】，错误的认为人生就是一场漫长的比赛，比赛项目是设定好的，而获胜者寥寥无几，人们因为害怕失败不敢尝试，不敢试错，一边陷入对竞争的焦虑和失败的恐惧，一边亦步亦趋的在一个外界固化的轨道上不停的奔跑。第二个【低关联假设】，认同这种假设的人认为，人生的种种经历之间相互独立，不存在什么关联。比如我上学时业余做了下电商，没人想到，求职时，HR更感兴趣的不是我的专业而是我电商的经验。第三个是心理学中的【僵固型心智】，认同这种假设的人会用一种固定的眼光看待自己的能力，很多人认为自己天赋不够，智商平平，学历不高，于是遇到一些挫折就自暴自弃。与之相对的是【成长型心智】，拥有这种心智的人认为智力和才能可以通过努力不断增长，因而不惧短期失败，反而越挫越勇，这样的人往往能获得更高的成就。只要我们能走出这些限制自己的假设，把人生看作一个不断发展提高的过程，允许自己在选择中犯错，从错误中学习，我们的人生会更精彩。3、即刻行动在我刚开始尝试写点东西的时候，常常因为没有思路一拖再拖，好几天出不了一篇稿，后来我开始想到什么就写点什么，让我写的动作先开始，思路居然自动涌出了。一件看上去繁难的事，只要开始做了，就会变得越来越容易。很多人都遇到这样的情况，有一个不错的想法，因为觉得还没有准备好，或者想要等等再做，没有即刻行动。结果这一等，就可能等了一辈子。我们永远都不可能完全准备好，总有我们还未估计的问题，总有我们能力上的短板，总有我们未搜集完全的信息，难道我们要一直准备下去吗？当然不是，只有你做出了实际的东西，你的思路才会更清晰，才能得以切实的锻炼，视野变得更加开阔。 当开始行动后，一定会遇到问题，其实遇到问题是好事，问题会让你更加清楚自己该做什么，问题也会推动你去寻找答案，当你解决了一个接一个的问题，回头看去发现自己居然提高了这么多。4、学习应该以问题为导向，以技能为目标我一直记得一句话：只有最后能够作用于现实的学习，才是唯一有效的学习。如何能让学习作用于现实？我觉得以问题为导向，以技能为目标是最好的方法。问题推动你去寻找解决方法，找到的解决方法是你要学会的技能，而通过技能解决问题的过程中，又会出现新的问题，于是一个正向循环开始了，就像是一个上坡的圆环，不断旋转不断提升。在学习的过程中，我们需要先打开新旧知识的通道，知道自己知道什么，同时知道自己不知道什么，才能有的放矢。因此，我们不妨多思考以下四个问题：1.针对当前的学习材料，我已具备了哪些相关知识？2.针对当前的学习材料，我又学到了哪些新的知识？这些知识对原有知识构成了何种补充或者挑战？3.针对当前的学习资料，还有哪些未知的东西，且这些东西我通过简单的探索就可以了解？4.针对当前的学习材料，还有哪些未知的东西，无法轻易地获得解答，同时又有价值成为我长期去探索地问题？互联网时代许多人养成了一种囤积癖，在网上下载很多东西，书籍、软件、电影、积累了很多G，放在那里懒得去看。事实上，我们在学习中也会遇到这种问题，很多人只关心，这本书，这篇文章说了什么，传递了哪些显而易见的知识。而只有少数人，才会去审视构成这些的材料，对它们进行本质和属性上的概括，评价它们的价值和意义。而只有非常少的人，会从一个创造者的角度去研究，把学习材料作为一个观摩与研究的范本来分析，将其拆开来看个究竟。想要真正学到知识的本质就得经过下面三个层次：1.它说了什么？给了我什么感受？（他原本就要传达的信息和知识）2.它是什么？对我有什么价值？（对内容的反思，以审视的目光对内容进行评价和定性）3.它的内部是什么组成的？它的效果是怎么实现的？（对形式和构成的洞察，研究内容表达的手法）当对知识进行了这样的解码，我们才能更快的掌握可以解决问题的技能。5、打磨思维利器过滤信息我们每天都处在信息过载中，要面对各种没有价值和没有意义的信息，它们或是无病呻吟、矫揉造作，或是充满了陈词滥调。它们占用了我们大量的时间和注意力，干扰我们的思考和记忆。我们不如给思维“断舍离”简化我们输入的信息，把时间放在更有价值的地方。想要过滤没有价值的信息，就要设置好我们大脑中的过滤器。1.精选可信赖的信息源：高质量的平台，可靠的媒体，官方的新闻，专注于某领域的大佬，虽然可能也会夹杂一些营销信息，但比搏人眼球的营销号，煽动情绪的自媒体好得多。2.不追逐当下流行或过热的信息：热点是噪音的重灾区，网上的热门话题多是人为制造的营销性话题，通过迎合人们猎奇心态来吸引注意，这些内容通常没有关注的必要，可以算是前文提到的短半衰期的事。3.重事实信息，轻观点和评论：事实信息是我们思考的基础材料，而各种观点和评论虽然有时给我们启发，但也会限制我们的思维。4.定期放空自己，屏蔽外界纷扰：无孔不入宣扬消费主义，传播焦虑等信息无孔不入，我们无法全部杜绝，这些信息让我们短视，追求短平快，忘掉自我。所以隔一段时间，就给自己一点时间，屏蔽外界的信息，让自己开始思考。锻炼思维方式为什么有的人可以看到隐藏在事件背后的本质，而有的人仅仅能看到表面的一点点杂质。最根本的原因是思维方式不同，具体的思维方式很多，这里就不一一列举了，不然文章要太过于冗长了，文末有我另一篇关于常用思维的回答。这里说说为什么要锻炼思维，以看到事物本质。我有一篇回答讲了，同龄人之间差异巨大是因为，个人天性和所处环境的共同作用，个体接收到外界环境的刺激，根据内在本性做出相应的反应，如果你能看到外界环境的本质，是不是做出的反应会更接近本质，是不是更能适应这个社会，同时更加清楚自己应该提升什么，而不是被表象的杂质蒙蔽双眼。6、努力的策略专注发展优势才能我们经常听到有人提起木桶效应，说木桶最短的一块板决定能装多少水。然而，木桶效应最早是类比团队管理的，对一个团队来说，决定其绩效的是最弱的那个成员。因为团队中每人各司其职，分工一环套一环，一个环节出现问题会影响其它环节。但是用在个体身上，这个理论并不合适，个体的短板和长板是不可分割的，我们看重一个人往往是因为他的突出才能。并且人的才能发展基于过往学习经验的积累，我们在才能上的学习积累呈S形曲线。浅尝辄止的人，很可能掌握了很多种技能却都停留在缓慢起步期从而一无所有，而专注投入走到最后的人将获得超额收益。设计挑战在发展优势才能的过程中，一定会遇到舒适区，你需要给自己设计挑战以便跳出这个舒适区走向更高的区域，当这个区域又变成舒适区了，再次向上挑战。这样一级一级的难度上去，会构成一个挑战阶梯。顺着这个阶梯就可能攀上才能的顶峰。著名的英语脱口秀译者谷大白话就是个很好的例子。他本是中医专业，因为个人兴趣开始自学英语。早期和很多人一样，背单词、考托福、GRE，当这些挑战都完成后，他开始听英语电台。刚开始感到很困难，但硬着头皮苦练几个月后，他发现一点难度都没有了。到此，他还没有停止，又开始听各种地方口音英语，之后开始观看英语脱口秀。英语脱口秀不仅语速快，还有很多Google都搜不到的俚语，只能靠深厚的文化背景和资料查找理解。最后这个难关也被他突破，他听译出了很少有人能译出的英文脱口秀节目。7、成为自我教育者大学时，一位老师告诉我，上学的目的不是为了让你学到多少知识，而是为了培养自我教育者。随着步入社会，更多的与这个世界交互，我才深刻理解这句话的含义。是的，知识是学不完的，并且学校的知识还有滞后性，一个新的技术出现，新的模式出现，到通过审核写进教科书，再到传输给学生经过的时间可能已经超过了5年，而小黄车从出现到上市再到倒闭，都用不了5年。学校教的东西也许你不感兴趣，学校教的东西也只是为了批量化生产各岗位的劳动者，想要自我提升，靠课本是远远不够的，必须保持持续不断，稳定的学习。所以，自我教育是我们必须要掌握的能力，做一个主动的探索者，敢于突破种种局限，不断试错，形成自己的优势甚至是独特的知识产权。希望你也能根据自己内心的需要，而不是外界的认同，做出独立的选择，甚至是做一些很酷的事，找到属于自己的路，把这条路走完，你一定能赢。引用1、自我提升和完善的四个方面2、有哪些方法可以提升自我
替换视频中的音频ffmpeg -i input.mp4 -i input.wav -c:v copy -map 0:v:0 -map 1:a:0 output.mp4如果音视频不一样长，则需要在输出文件名之前添加选项-shortest自动剪掉末尾超出的音频或视频视频片段剪辑ffmpeg -ss 00:00:00 -t 00:01:00 -i input.mp4 -c copy output.mp4ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -c copy output.mp4指定剪切的时间参数最好放到输入文件之前，这样虽然剪切出来的时间不精确但是没有黑屏-ss：指定开始时间-t：指定持续时间-to：指定结束时间-c：使用原视频编码方式截图ffmpeg -i input.mp4 -f image2 -vf fps=fps=1 out%d.pngffmpeg -i input.mp4 -f image2 -vf fps=fps=1/20 out%d.png每隔1秒或20秒截一张图视频图片互转ffmpeg -i input.mp4 out%4d.pngffmpeg -r 30 -i input%4d.png -c:v libx264 -crf 20 -pix_fmt yuv420p -preset slow output.mp4-r：指定视频帧率-pix_fmt：色彩格式，视频一般是yuv420p的，png图片是rgb24的转码ffmpeg -i input.mp4 -c:v libx264 -profile:v high -level 5.1 -crf 20 -preset slower output.mp4ffmpeg -i input.mp4 -c:v libx265 -x265-params level=6.2 -crf 20 -preset slow output.mp4-c:v：指定视频的编码格式-qp：固定量化值（Constant Quantizer）：每一帧都是相同的画质，值为0-51，越小质量越好（0是无损）-crf：恒定速率因子（Constant Rate Factor），在所选的画质上下波动，值为0-51，越小质量越好（0是无损）-preset：调节编码速度和质量的平衡，可选ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo，从快到慢。ffmpeg默认采用crf方式编码视频关键帧无损视频分割ffmpeg -i input.mp4 -f segment -segment_times 00:00:10,00:00:20 -c copy %02d.mp4 ffmpeg -i input.mp4 -f segment -segment_time 00:00:5 -c copy %02d.mp4第一个命令是按照5秒和10秒的时间点进行分割，第二个命令是按照5秒间隔时间进行分割。%02d.mp4表示按照序号（从0开始）以2位宽度输出文件名（结果形如00.mp4，01.mp4，02.mp4）-segment_time：指定分割的间隔时间-segment_times：指定分割的时间点，多个时间点以逗号隔开如果分割时间点不是关键帧，将向后进行搜索到第一个关键帧无损视频合并相同规格的视频片段可以进行合并，先创建list.txt文件，在文件中写入要合并的文件名称，例如：file 00.mp4 file 01.mp4 file 02.mp4再使用以下命令进行无损合并：ffmpeg -f concat -i list.txt -c copy output.mp4记录合并视频列表的文本文件可以是任意名称的（不是list.txt也行）