实践指南:EdgeOne与HAI的梦幻联动

news2024/11/24 22:29:50

在当今快速发展的数字时代,安全和速度已成为网络服务的基石。EdgeOne,作为腾讯云提供的边缘安全加速平台,以其全球部署的节点和强大的安全防护功能,为用户提供了稳定而高效的网络体验。而HAI(HyperApplicationInventor),腾讯云推出的高性能应用服务,通过其易用的图形化界面和丰富的模型库,使得AI应用开发变得触手可及。本文将探讨EdgeOne与HAI的结合如何为用户提供一个既安全又高效的AI应用开发环境。

EdgeOne:全球加速与安全防护

image

  1. DDoS防护:EdgeOne使用Anycast架构在全球建立超过25个清洗中心,能快速检测并清除网络、传输和应用层的DDoS攻击。
  2. WEB攻击防护:EdgeOne拥有广泛的攻击特征库,覆盖主要的安全威胁,能阻止各类Web攻击并防御0day漏洞,通过AI提高检测准确性,减少误报。
  3. 智能CC识别:EdgeOne利用多年经验,通过分析网络流量和多种参数,自动识别并定位攻击源,有效对抗CC攻击。
  4. BOT防护:EdgeOne通过协议、IP和会话特征识别BOT,结合数据分析建立模型,防止恶意爬虫攻击,减少对正常爬虫的误伤。
  5. 攻击溯源:EdgeOne可以捕获并分析异常事件,提取攻击信息,提供监控页面展示攻击详情,帮助用户调整防护策略。
  6. 全天候监测:腾讯安全团队24/7实时监控,主动发现并应对威胁,快速响应安全事件。

不多说了,剩下的内容都已经在官方文档中有详细描述。就我们个人而言,EdgeOne具有以下主要优点,并且我也附上了需要的关键文档地址:

  • 干净流量计费模式:对于安全防护功能拦截的请求不进行计费。这种计费模式的优势在于,用户无需担心因网络攻击而产生的额外费用,可以更加专注于业务的发展和优化。
  • 免费证书:简化HTTPS部署,官方文档:https://cloud.tencent.com/document/product/1552/90437
  • 个人版套餐升级:CC防护与自定义规则设置,官方文档:https://cloud.tencent.com/document/product/1552/93128
  • 无域名接入:快速启用四层代理服务,这个得需要企业认证,个人慎选。官方文档:https://cloud.tencent.com/document/product/1552/96051
  • 自助诊断:快速定位问题,通过输入诊断链接,用户可以快速获得诊断报告和解决指引,从而提高问题解决的效率。官方文档:https://cloud.tencent.com/document/product/1552/81229
  • 折扣活动:年度最低价,性价比之选,官方文档:https://cloud.tencent.com/document/product/1552/96920
  • 无域名接入功能,快速接入 EdgeOne 启用四层代理服务。https://cloud.tencent.com/document/product/1552/96051

HAI:AI应用的快速开发

HAI提供了可视化交互界面,支持JupyterLab、WebUI等多种算力连接方式,使得用户即使没有深厚的编程背景也能轻松开发AI应用。这种“有手就能开发”的设计理念,极大地降低了AI技术的应用门槛。支持学术加速,通过线路自动择优,能够大幅提升主流学术资源平台的访问、下载速度。同时,HAI预装了StableDiffusion、ChatGLM等热门模型,用户可以在数分钟内构建自己的大语言模型、AI作画等应用环境。

这个话题不需要详细讨论了,我已经整理了开发中需要了解的主要优点和相关文档,以便大家能够快速入手开发:

  1. 快速部署:创建高性能应用服务实例,选择合适的地域、算力方案,并设置实例名称和硬盘空间。官方文档:https://cloud.tencent.com/document/product/1721/101036
  2. 生成API文档:在已部署的Stable Diffusion应用基础上,通过JupyterLab部署API服务。官方文档:https://cloud.tencent.com/document/product/1721/102198
  3. 模型下载地址:https://civitai.com/
  4. 导入外部模型:在腾讯云高性能应用服务HAI上,可以通过三种方式导入外部模型,但是为了花钱少一点,我推荐你选择COS存储。官方文档:https://cloud.tencent.com/document/product/1721/102523#1bb64c7b-0bc8-4a2a-86f0-ad0e71d8f059

联动开发

现在让我们来探讨一下为何可以采用联动开发的方式。我们的目标是以最为经济实惠的价格部署我们的AI绘画应用。

  1. HAI高性能服务器是按照小时收费的,所以我们很多情况下会选择关机,但是一旦重启服务器,外网IP就会变动,导致你的访问路径也就随之变换。但是这一步可以在EdgeOne中使用边缘函数解决。
  2. EdgeOne本身就已经对我们的服务器做了相应的安全防护。
  3. HAI高性能服务器,他是一个Ubuntu20.04环境的虚拟机,也就是说,我们已经有了一个服务器,只不过这个服务器被包装成了绘画AI应用,所以不要浪费掉。用起来

我们接下来看下我们会用到哪些技术:HTML、Javascript、CSS、Nginx、Python、边缘函数中的各个API、Linux基本命令。

准备环境

HAI高性能服务器

关于创建服务器的步骤,我就不再进行演示了,请直接查看官方教程,相关地址也已经提供。我们将重点演示以下内容:下载/上传模型、通过WEB UI演示AI绘画功能、下载并配置Nginx以监听80端口、启动API接口服务。

下载/上传模型

这一步我当时使用过如何官方默认的JupyterLab页面上传,速度是真的很可观~我直接放弃了。选择了COS对象存储。我们先去下载好看的模型。这里以https://civitai.com/ 为例。

image

模型下载到本地之后,去创建COS对象存储服务。

如何创建这么简单的教程,我就不演示了,我们直接看上传和下载。这里创建存储桶的时候,所属地域一定要和你HAI绘画服务器在一起。否则会产生很多额外的花销。谨记~

image

进入存储桶之后,直接点击页面的上传文件即可,请选择刚才下载的模型文件。上传的过程中不要动浏览器,一旦刷新和关闭浏览器就会前功尽弃。

image

稍等片刻,然后点击模型文件的详情,进入基本信息页面。在这里,请确保将权限设置为公有读取,否则HAI服务器将无法下载到模型文件。然后,复制临时链接。

image

我们进入已经创建好的HAI服务器。

image

image

image

进入终端,使用一下几个命令,下载模型文件到模型文件夹。

cd stable-diffusion-webui/models/Stable-diffusion

wget 临时链接粘贴到此处

等待下载完毕。

WEB UI演示AI绘画

让我们前往WEB UI页面,尝试一下看看是否成功安装了。

image

image

在WEB UI页面上,选择您自己的模型文件,并使用一段神奇的提示词。当然,提示词越好,生成的结果也会越好。如果有任何不清楚的地方,您可以参考您下载的模型附带的教程。这样基本上就没有问题了。接下来,让我们再来看看如何让HAI提供站点服务。

Nginx站点配置

依然是同样的步骤,在JupyterLab中的终端中直接使用以下命令操作:

  1. 下载Nginx:使用命令 apt install nginx 进行安装。
  2. 启动Nginx:使用命令 service nginx start 启动服务。

当您直接使用IP地址在浏览器中访问后,如果显示以下界面,那就表示安装成功了。

image

接下来,我们继续修改Nginx的配置文件:
使用以下命令编辑配置文件:

vim /etc/nginx/sites-available/default

然后,在文件中添加以下配置:

image

# 配置/images路径的访问规则
location /images/  {
            add_header 'Access-Control-Allow-Origin' "$http_origin";
            add_header 'Access-Control-Expose-Headers' 'strict-origin-when-cross-origin';
            alias   /root/stable-diffusion-webui/outputs/star/images/;  # 你的静态页面
            expires 1d;
            }
location /sdapi/  {
      # Add CORS headers
     add_header 'Access-Control-Allow-Origin' '*' always;
     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
     add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    proxy_pass  http://127.0.0.1:7862/sdapi/;
    if ($request_method = "OPTIONS"){
            return 200;
        }
    }

这里配置了/images路径的访问规则以及API服务的代理。为什么要配置代理呢?这显然是因为绘画的API服务不支持跨域访问,所以我们添加了一个反向代理。由于我们的请求是POST非简单请求,因此浏览器还会发送一个OPTIONS的预检请求。我也做了相应的处理。以免跨域报错。

修改完成后,使用命令 service nginx reload 来重新加载Nginx的配置。您可以看到我们配置的/images路径对应的文件。这个路径您可以随心配置,只要确保其中包含您自己的图片即可。您可以选择自己维护这些图片,也可以通过程序生成。为了演示,我只简单复制了几张我使用AI绘画出来的图片。

image

让我们通过Nginx来访问一下图片,以确保可以正常访问。

在浏览器中输入以下地址进行访问:http://你的IP/images/1.png

image

接下来,我们继续调试API服务。请启动HAI绘画应用的API服务,使用以下命令:

python launch.py --nowebui --xformers --opt-split-attention --listen --port 7862

然后,在浏览器中输入IP地址和端口号,后面加上/docs路径即可正常访问路径了。

image

让我们通过工具来测试一下,看看能否通过Nginx正常访问接口。你可以随意选择一个接口路径进行测试。我选择了一个简单的无请求参数的GET请求作为示例。通常情况下,这个请求会带有端口号。

image

使用调试工具来调用一下Nginx配置的路径,确保一切正常。

image

如果您还不确定的话,可以通过查看默认的Nginx访问路径来确认。您可以使用以下命令来实时查看日志:

tail -f /var/log/nginx/access.log

这样您就可以确定API是否能够被正确命中。

image

启动API接口服务

为了解决Nginx每次关机后无法正常启动的难题,我们采取了一项巧妙的解决方案:将启动命令直接写入API启动服务接口中,从而确保服务能够在每次启动时正确配置Nginx。具体操作如下:首先,我们需要找到项目中的launch.py文件。一旦找到了该文件,我们只需在其中添加几行命令即可。这些命令将确保Nginx在服务器启动时得到正确的配置,并能够顺利启动。这种方法简单而高效,能够有效解决Nginx启动问题,保证服务的稳定性和可靠性。

image

代码如下:

import subprocess

# 此处省略很多代码~~
def main():
    # 使用subprocess.run()执行命令
    result = subprocess.run(['service', 'nginx', 'start'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    
    # 检查命令是否执行成功
    if result.returncode == 0:
        print("Nginx 已成功启动")
    else:
        print("启动 Nginx 时出错")
        print("错误信息:", result.stderr)
        
# 此处省略很多代码~~

为了验证我们的解决方案的有效性,让我们再次重新启动API服务,观察是否能够成功启动Nginx。

image

通过以上步骤,我们的HAI高性能服务器已经完成了基本配置。无论进行多少次关机重启,我们的Nginx都会随着API接口一起启动,确保了系统的稳定性和可靠性。现在,让我们继续深入探讨EdgeOne的配置过程。

EdgeOne边缘函数

在配置EdgeOne时,首先需要确保完成无域名站点的配置,你可以直接参考官方文档进行操作。接着,我们需要创建边缘函数来实现相应的缓存处理。考虑到你没有企业版权限,因此无法使用四层加速,但可以通过边缘函数来达到类似的效果。关于边缘函数中代码的具体使用问题,我建议你仔细阅读官方文档,其中包含了详细的操作指南和示例代码,可以帮助你快速上手。你可以点击以下链接查看官方文档:EdgeOne 官方文档

image

在配置边缘函数时,你可以选择Hello World模板作为起点,然后依次点击下一步进行配置。虽然选择了Hello World模板,但它只是一个起点,我们将在此基础上进行深入的定制,以实现我们所需要的功能。

image

image

完成所有配置后,系统会为你生成一个默认的域名。接下来,我们需要编写代码来实现功能。通常情况下,我会额外编写一个test函数,专门用于测试。当然,如果你觉得不需要,也可以忽略。在这次配置中,我们创建了两个边缘函数。这些函数已经经过我封装,通过最小的变动来应对HAI服务器外网IP的变化,确保了系统的稳定性和可靠性。

查询图片处理

一个绘画应用的关键之一在于能够提供优质的图片展示功能。毕竟,画作的视觉效果是吸引用户的关键之一。缺少了图片展示功能,这个应用就好比是一本没有插图的画册,缺乏吸引力,难以引起用户的兴趣。

async function fetchJquery(event, request) {
  const cache = caches.default;
  // 缓存没有命中,回源并缓存
  let response = await fetch(request);

  // 在响应头添加 Cahe-Control,设置缓存时长 10s
  response.headers.append('Cache-Control', 's-maxage=600');
  event.waitUntil(cache.put(request, response.clone()));

  // 未命中缓存,设置响应头标识
  response.headers.append('x-edgefunctions-cache', 'miss');
  return response;
}

async function handleEvent(event) {
  const urlInfo = new URL(event.request.url);
  var url = new URL(event.request.url);
  var ip = url.searchParams.get('ip');
  // 请求非图片资源
  if (!/\.(jpe?g|png)$/.test(urlInfo.pathname)) {
    return event.respondWith(new Response('Error thrown 没命中图片URL' + urlInfo.pathname));
  }
  // 资源地址,也作为缓存键
  const request = new Request(ip + urlInfo.pathname);
  // 缓存默认实例
  const cache = caches.default;

  try {
    // 获取关联的缓存内容,缓存过,接口底层不主动回源,抛出 504 错误
    let response = await cache.match(request);

    // 缓存不存在,重新获取远程资源
    if (!response) {
      return fetchJquery(event, request);
    }

    // 命中缓存,设置响应头标识
    response.headers.append('x-edgefunctions-cache', 'hit');

    return response;
  } catch (e) {
    await cache.delete(request);
    // 缓存过期或其他异常,重新获取远程资源
    return fetchJquery(event, request);
  }
}

addEventListener('fetch', (event) => {
  event.respondWith(handleEvent(event));
});

这段代码的主要功能是用于处理网络请求并管理资源的缓存策略,确保快速响应用户请求的同时,减少不必要的网络流量。

  1. fetchJquery 函数:
    • 该函数用于处理当资源未被缓存或缓存过期时的情况。
    • 使用fetch函数发起网络请求,获取资源的最新响应。
    • 在响应头中添加Cache-Control,设置资源在服务端缓存中的最长有效时间为600秒。
    • 使用cache.put方法将获取的响应克隆一份并存入缓存中,以便后续请求可以直接从缓存中获取。
    • 设置响应头x-edgefunctions-cachemiss,表示这是一个未命中缓存的请求。
    • 返回原始响应给请求者。
  2. handleEvent 函数:
    • 该函数用于检查请求的URL是否指向一个图片资源(通过文件扩展名判断)。
    • 如果请求的不是图片资源,将返回一个错误响应。
    • 如果是图片资源,尝试从缓存中获取请求的资源。
    • 如果缓存中存在资源,则设置响应头x-edgefunctions-cachehit,表示这是一个命中缓存的请求,并返回缓存中的资源。
    • 如果缓存中不存在资源或发生异常(如缓存过期),则删除缓存并重新调用fetchJquery函数获取资源。

在访问图片资源时,需要特别注意的是,我采用了路径后面的 IP 参数进行访问。这是因为 HAI 每次重启后外网 IP 都会发生变化,如果每次都要修改每个边缘函数的话,会相当繁琐。为了避免这种情况,我选择通过参数传递的方式来获取图片资源。这样一来,获取图片资源的边缘函数就不需要再进行任何修改了。另外,你还需要对匹配触发规则进行配置,比如我设置的规则是包含 “/images” 的路径才会触发相应的操作。

image

在浏览器中输入默认域名,并随意添加 “/images/*.png” 即可访问相应图片,前提是该图片存储于我们的服务器上。现在让我们来观察一下效果。值得注意的是,这种方式的命中率并不是很高,所以在刚部署完边缘函数后,需要多刷新几次才能看到效果。

image

静态网页

这个边缘函数专门用于处理用户交互。

const html = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI绘图</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        
        .container {
            display: flex;
            justify-content: center;
        }

        .box {
            margin: 20px;
            border: none; /* 移除框的边框 */
            padding: 10px;
            background-color: #f5f5f5; /* 设置框的背景颜色 */
            border-radius: 10px; /* 添加圆角 */
            display: flex; /* 使用flex布局 */
            flex-direction: column; /* 垂直布局 */
            align-items: center; /* 居中对齐 */
            text-align: center; /* 文字居中对齐 */
        }
        .box:nth-child(2) {
          align-self: start; /* 右边的box顶部对齐 */
        }

        .box-title {
            margin-bottom: 10px;
            margin-right: 500px;
            border-bottom: 1px solid grey; /* 添加标题底部的分隔线 */
            padding-bottom: 5px; /* 添加一些底部间距以增强视觉效果 */
        }

        #image-container {
            width: 512px;
            height: 512px;
            background-color: #eee;
            margin: auto; /* 在父元素中水平居中 */
        }

        #text-description {
            width: 300px;
            height: 100px;
            margin-bottom: 10px;
            border: 1px solid grey; /* 添加外边框 */
            padding: 5px;
            resize: none; /* 禁止调整大小 */
            font-size: 14px;
            outline: none; /* 移除输入框默认的外边框 */
            border-radius: 0; /* 移除圆角 */
        }
        
        #text-description2 {
            width: 300px;
            height: 100px;
            margin-bottom: 10px;
            border: 1px solid grey; /* 添加外边框 */
            padding: 5px;
            resize: none; /* 禁止调整大小 */
            font-size: 14px;
            outline: none; /* 移除输入框默认的外边框 */
            border-radius: 0; /* 移除圆角 */
        }
        
        #dropdown {
            padding: 10px 20px; /* 增大按钮的内边距 */
            cursor: pointer;
            margin-bottom: 10px;
            border: 1px solid grey; /* 添加外边框 */
            width: 100%; /* 将宽度设置为100%以与容器对齐 */
            align-self: flex-start; /* 按钮左对齐 */
        }
        
        #dropdown2 {
            padding: 10px 20px; /* 增大按钮的内边距 */
            cursor: pointer;
            margin-bottom: 10px;
            border: 1px solid grey; /* 添加外边框 */
            width: 100%; /* 将宽度设置为100%以与容器对齐 */
            align-self: flex-start; /* 按钮左对齐 */
        }
        
        #acckey {
            padding: 10px 20px; /* 增大按钮的内边距 */
            cursor: pointer;
            margin-bottom: 10px;
            border: 1px solid grey; /* 添加外边框 */
            width: 100%; /* 将宽度设置为100%以与容器对齐 */
            align-self: stretch; /* 按钮左对齐 */
        }

        #input-container {
            flex-grow: 1;
            display: flex;
            flex-direction: column; /* 垂直布局 */
            align-items: center; /* 居中对齐 */
        }

        #text-input {
            width: 300px;
            height: 30px;
            margin-bottom: 10px;
        }

        #generate-button {
            padding: 10px 20px; /* 增大按钮的内边距 */
            background-color: #4caf50;
            color: white;
            border: none;
            cursor: pointer;
            width: 100%; /* 将宽度设置为100%以与容器对齐 */
            align-self: flex-start; /* 按钮左对齐 */
        }
        
        @media (max-width: 600px) {
            .container {
                flex-wrap: wrap; /* 在屏幕宽度不足时换行显示 */
            }

            .box {
                width: 100%; /* 让框占满一行 */
                margin-bottom: 20px; /* 添加底边距 */
            }
        }
        .image-list {
            display: flex; /* 使用flex布局 */
            flex-wrap: wrap; /* 允许列表换行 */
            justify-content: space-around; /* 间隔均匀分布 */
            align-items: center; /* 垂直居中对齐 */
        }

        .image-list img {
            max-width: 18%; /* 图片最大宽度占比,根据需要调整 */
            margin: 5px; /* 图片之间的间隔 */
            object-fit: contain; /* 确保图片等比例缩放 */
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="box">
            <h2 class="box-title">绘图结果</h2>
            <div id="image-container">
            </div>
        </div>
        <div class="box">
            <h2 class="box-title">文本描述</h2>
            <div id="input-container">
                <textarea id="text-description" title="文本描述" placeholder="文本描述。
算法将根据输入的文本智能生成与之相关的图像。建议详细描述画面主体、细节、场景等,文本描述越丰富,生成效果越精美。
不能为空,推荐使用中文。最多传512个字符。"></textarea>
                <textarea id="text-description2" title="反向文本描述" placeholder="反向文本描述。
用于一定程度上从反面引导模型生成的走向,减少生成结果中出现描述内容的可能,但不能完全杜绝。
推荐使用中文。最多传512个字符。"></textarea>
                <select id="dropdown" title="分辨率">
                  <option value="768:1024">分辨率(默认768:1024)</option>
                  <option value="768:768">768:768</option>
                  <option value="1024:768">1024:768</option>
                </select>
                <select id="dropdown2" title="绘画风格">
                  <option value="201">绘画风格(默认日系动漫风格)</option>
                </select>
                <div style="display: flex; justify-content: flex-start; width: 100%;">
                  <input id="acckey" type="password" placeholder="访问密钥" style="width: 100%;">
               </div>
                <button id="generate-button">生成</button>
            </div>
        </div>
        
    </div>
    <!-- 图片列表容器 -->
    <div class="image-list" id="image-list">
        <!-- 图片将被动态插入到这里 -->
    </div>
    <script>
    // 页面加载完成后执行的函数
        window.onload = function() {
            // 假设您已经有了一个函数来发起请求和处理响应
            // 这里我们直接调用这个函数
            createImageList();
        };
    var ip = "http://101.43.51.133";
  document.getElementById("generate-button").addEventListener("click", function() {
    var Ai_Image_Prompt = document.getElementById("text-description").value;
    var Ai_Image_NegativePrompt = document.getElementById("text-description2").value;
    var Ai_Image_Size = document.getElementById("dropdown").value;
    var Ai_Image_Styles = document.getElementById("dropdown2").value;
    var Ai_Image_AccKey = document.getElementById("acckey").value;


    var data = {
      "denoising_strength": 0,
      "override_settings": {
        "sd_model_checkpoint" :"animagineXLV31_v30.safetensors [1449e5b0b9]"
     }, 
      "prompt": Ai_Image_Prompt,
      "negative_prompt": Ai_Image_NegativePrompt,
      "seed": 0,
      "batch_size": 1,
      "n_iter": 1,
      "num_inference_steps": 28,
      "guidance_scale": 7,
      "sdxl_style": "(None)",
      "quality_tags": "(None)",
      "width": 512,
      "height": 512,
      "sampler": "Euler a"
    };

    var xhr = new XMLHttpRequest();
    xhr.open("POST", ip + "/sdapi/v1/txt2img", true);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.onreadystatechange = function() {
        if(xhr.readyState === 4){
      if (xhr.status === 200) {
        var response = JSON.parse(xhr.responseText);
          var imageContainer = document.getElementById("image-container"); 
          // 创建一个新的img元素
          var imgElement = document.createElement("img");
          imageContainer.style.backgroundSize = "contain";
            // 设置img元素的src属性为base64编码的图片数据
            imgElement.src = 'data:image/png;base64,'+response.images[0];
            // 设置img元素的alt属性
            imgElement.alt = "动态生成的图片";

            // 将img元素添加到imageContainer中
            imageContainer.appendChild(imgElement);
            // 由于img元素是异步加载的,我们需要等待图片加载完成后再进行操作
      }else{
          alert("请求错误:" + xhr.status);
      }
    }
    };
    xhr.onerror = function() {
        // 处理网络错误
        alert("网络错误");
    };
    xhr.send(JSON.stringify(data));
  });
  // 处理图片列表的函数
    function createImageList() {
        var imageList = document.getElementById("image-list");
        imageList.innerHTML = ""; // 清空当前列表
        // 使用for循环生成图片元素
        for (var i = 1; i <= 5; i++) {
            // 创建新的img元素
            var img = document.createElement("img");
            // 设置图片的src属性为对应的图片文件路径
            // 假设所有图片都存放在同一个目录下
            img.src = "http://ai-zone-2vjn2f1z4giq-1302107156.eo-edgefunctions.com/images/" + i + ".png?ip="+ip;
            img.alt = "Image " + i; // 设置alt属性
            img.style.width = '100%'; // 设置图片宽度为100%

            // 将img元素添加到图片列表容器中
            imageList.appendChild(img);
        }
    }
</script>

</body>
</html>

`;
async function handleRequest(request) {
return new Response(html, {
headers: {
'content-type': 'text/html; charset=UTF-8',
'x-edgefunctions-test': 'Welcome to use Edge Functions.',
},
});
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});

这段代码定义了一个HTML页面,用于展示一个AI绘图应用的用户界面,并包含了与之交互的JavaScript代码。主要功能和组成部分如下:

  1. HTML结构
    • 页面包含一个容器container,用于布局两个主要部分:绘图结果展示和文本描述输入。
    • 绘图结果部分包含一个标题和一个图像容器image-container,用于展示AI生成的图像。
    • 文本描述部分包含两个文本区域text-descriptiontext-description2,供用户输入正向描述和反向描述,用于指导AI绘图。
    • 一个按钮generate-button,用于触发AI绘图过程。
  2. JavaScript逻辑
    • 页面加载完成后,调用createImageList函数,动态生成一个图片列表并展示在页面上。
    • 监听generate-button按钮的点击事件,收集用户输入的描述和选择的参数,构造一个AI绘图请求,并发送到服务器。
    • 使用XMLHttpRequest对象发送POST请求到指定的服务器地址,处理响应并动态更新页面上的图像容器以展示生成的图像。

我们看一下最终效果:

image

我已经将 IP 地址单独抽离出来,以确保最小化变动量。如果需要重启,只需更新 IP 地址,然后重新部署即可重新访问。

image

让我们再次观察一下生成图片的效果。点击生成时,后台接口的响应也都是正常的。

image

image

质量确实还有提升空间,我仔细检查了一下,发现问题可能出在模型的限制上。例如,对于宽高比不能是正方形的图像,可以稍微宽一点或高一点。这样生成的图像会更具观赏性。但这些问题都是可以通过调试解决的。总的来说,我们的EdgeOne和HAI的梦幻联动已经取得了阶段性的成果,算是告一段落了。

总结

本篇文章源于他人创意的启发与深入思考,我投入了大量时间与精力,致力于EdgeOne与HAI之间的协同调试,克服了众多技术奇葩问题。通过不懈的努力,成功地将这两个强大的平台整合在一起,打造出一个还不错的参考案例。这次探索也是我个人首次涉足边缘函数式产品的实践之旅。我希望通过分享这次的经历和成果,能够激发更多人对这些前沿技术的兴趣和探索。如果你对我的项目感兴趣,或者从中获得了灵感与帮助,不妨点赞支持,也欢迎关注我,一起交流学习,共同进步。

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

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

相关文章

【H2O2|全栈】JS进阶知识(六)ES6(2)

目录 前言 开篇语 准备工作 Set和Map 基本概念 Set 相互转化 常见属性和API 数组去重 并集、交集和差集 Map 转化 常见的属性和API Set和Map的区别 This的指向 function函数 箭头函数 修改this 使用方式 三种方式的异同 案例 更改this指向为obj 求数组数…

Redis配置主从架构、集群架构模式 redis主从架构配置 redis主从配置 redis主从架构 redis集群配置

Redis配置主从架构、集群架构模式 redis主从架构配置 redis主从配置 redis主从架构 redis集群配置 1、主从模式1.1、主节点配置1.2、从节点配置1.3、测试 2、集群模式 1、主从模式 1.1、主节点配置 # 监听所有网络接口 bind 0.0.0.0# cluster-enabled表示为集群模式&#xff…

人工智能深度学习-前置-Torch框架

PyTorch是一个基于Python的深度学习框架&#xff0c;它提供了一种灵活、高效、易于学习的方式来实现深度学习模型。PyTorch最初由Facebook开发&#xff0c;被广泛应用于计算机视觉、自然语言处理、语音识别等领域。 安装 建议创建一个新的conda虚拟环境来安装pytorch&#xf…

vue3+ts el-tabel 搜索组件

爷爷页面 <template> <searchstyle"z-index: 9999":options"options"placeholder"请选择时间&#xff0c;或输入名称、单选、多个勾选、模糊查询"search"onSearch"></search> </template> <script lan…

Proteus 8.17的详细安装教程

通过百度网盘分享的文件&#xff1a;Proteus8.17(64bit&#xff09;.zip 链接&#xff1a;https://pan.baidu.com/s/1zu8ts1Idhgg9DGUHpAve7Q 提取码&#xff1a;8q8v 1.右击【Proteus8.17(64bit&#xff09;.zip】&#xff0c;选择【全部解压缩......】。 &#xff0c; 2.…

Django基础配置

一.前言 前面我们说完了前端基础&#xff0c;现在我们开始讲后端框架了&#xff0c;我们今天说的是django&#xff0c;当然今天主要还是和大家了解一下框架和django的基础配置 二.web框架 2.1 web框架初始 在我们学习web框架的时候&#xff0c;我们首先得了解到web框架的本…

Keepalived部署

Keepalived部署 安装配置单VIP模式配置master节点查看节点IP信息配置 keepalived.conf启动且加入开机自启查看是否生效 配置backup节点配置 keepalived.conf启动且加入开机自启查看是否生效 主备测试 多VIP配置 keepalived.conf查看IP 安装 dnf install -y keepalived配置 单…

Oracle JDK(通常简称为 JDK)和 OpenJDK区别

Java 的开发和运行时环境主要由两种实现主导&#xff1a;Oracle JDK&#xff08;通常简称为 JDK&#xff09;和 OpenJDK。尽管它们都基于同一个代码库&#xff0c;但在一些关键点上有所区别。以下是详细的对比&#xff1a; 1. 基础代码 Oracle JDK&#xff1a; 基于 OpenJD…

qt+opengl 三维物体加入摄像机

1 在前几期的文章中&#xff0c;我们已经实现了三维正方体的显示了&#xff0c;那我们来实现让物体的由远及近&#xff0c;和由近及远。这里我们需要了解一个概念摄像机。 1.1 摄像机定义&#xff1a;在世界空间中位置、观察方向、指向右侧向量、指向上方的向量。如下图所示: …

ASCB1系列APP操控末端回路智能微断 物联网断路器 远程控制开关 学校、工厂、农场、商业大楼等可用

安科瑞戴婷 Acrel-Fanny ASCB1系列智能微型断路器是安科瑞电气股份有限公司全新推出的智慧用电产品&#xff0c;产品由智能微型断路器与智能网关两部分组成&#xff0c;可用于对用电线路的关键电气因素&#xff0c;如电压、电流、功率、温度、漏电、能耗等进行实时监测&#x…

JMeter监听器与压测监控之 InfluxDB

1. 简介 在本文中&#xff0c;我们将介绍如何在 Kali Linux 上通过 Docker 安装 InfluxDB&#xff0c;并使用 JMeter 对其进行性能监控。InfluxDB 是一个高性能的时序数据库&#xff0c;而 JMeter 是一个开源的性能测试工具&#xff0c;可以用于对各种服务进行负载测试和性能监…

[安洵杯 2019]iamthinking-parse_url绕过thinkphp6.0反序列化

/www.zip下载源码 查看序列化点&#xff0c;index.php <?php namespace app\controller; use app\BaseController;class Index extends BaseController {public function index(){echo "<img src../test.jpg"."/>";$paylaod $_GET[payload];i…

解决CondaError: argument COMMAND: invalid choice: ‘activate‘

自上篇系统重装后&#xff0c;Anaconda重新导入后终端进入conda环境报错&#xff1a; conda-script.py: error: argument COMMAND: invalid choice: ‘activate’ C:\Windows\system32>conda activate xin usage: conda-script.py [-h] [--no-plugins] [-V] COMMAND ... …

【CSS3】Flex弹性布局

文章目录 前言一、基本概念1.容器和项目&#xff1a;2.主轴和交叉轴&#xff1a; 二、容器属性1.flex-direction&#xff1a;决定主轴的方向&#xff0c;即x轴还是y轴2.flex-wrap&#xff1a;定义项目的换行方式3.flex-flow&#xff1a;flex-direction属性和flex-wrap属性的简写…

简述C++map容器

pair键值对 std::pair在很多关联容器&#xff08;如std::map、std::multimap、std::set、std&#xff1a;multiset等&#xff09;中被广泛应用。以std::map为例&#xff0c;std::map是一个键值对的容器&#xff0c;其中每个元素都是一个std::pair&#xff0c;键用于唯一标识元…

刷题计划 day22回溯(一)【组合】【组合总和 III】【电话号码的字母组合】

⚡刷题计划day22 回溯&#xff08;一&#xff09;开始&#xff0c;此期开启回溯专题&#xff0c;敬请期待关注&#xff0c;可以点个免费的赞哦~ 往期可看专栏&#xff0c;关注不迷路&#xff0c; 您的支持是我的最大动力&#x1f339;~ 目录 回溯算法理论基础 回溯法解决的…

Qt桌面应用开发 第六天(鼠标事件 定时器事件 定时器类 事件分发器 事件过滤器)

目录 1.1鼠标进入和离开enterEvent\leaveEvent 1.2鼠标按下释放和移动mousePressEvent\mouseReleaseEvent\mouseMoveEvent 1.3定时器事件timerEvent 1.4定时器类QTimer 1.5事件分发器event 1.6事件过滤器eventFilter 1.1鼠标进入和离开enterEvent\leaveEvent 事件&#x…

【Linux清空显存占用】Linux 系统中清理 GPU 显存

操作指令 # 查看NVIDIA GPU状态和进程 nvidia-smi # 查找所有包含"python"的进程 ps -ef | grep python # 强制结束进程号为3023的进程 kill -9 3023截图演示 在 Linux 系统中清理 GPU 显存可以采用以下方法&#xff1a; 1. 终止特定进程&#xff08;常用方法&…

对撞双指针(七)三数之和

15. 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组…

vue2-基础核心

vue简介 动态构建用户界面的渐进式 JavaScript 框架 vue的特点: 遵循MVVM模式 采用组件化模式&#xff0c;提高代码复用率&#xff0c;让代码更好维护 声明式编码&#xff0c;无需直接操作DOM&#xff0c;提高开发效率&#xff0c;编码简洁、体积小&#xff0c;运行效率高 本…