在當今快速發展的數字時代,安全和速度已成為網路服務的基石。EdgeOne,作為騰訊雲提供的邊緣安全加速平臺,以其全球部署的節點和強大的安全防護功能,為使用者提供了穩定而高效的網路體驗。而HAI(HyperApplicationInventor),騰訊雲推出的高效能應用服務,透過其易用的圖形化介面和豐富的模型庫,使得AI應用開發變得觸手可及。本文將探討EdgeOne與HAI的結合如何為使用者提供一個既安全又高效的AI應用開發環境。
EdgeOne:全球加速與安全防護
- DDoS防護:EdgeOne使用Anycast架構在全球建立超過25個清洗中心,能快速檢測並清除網路、傳輸和應用層的DDoS攻擊。
- WEB攻擊防護:EdgeOne擁有廣泛的攻擊特徵庫,覆蓋主要的安全威脅,能阻止各類Web攻擊並防禦0day漏洞,透過AI提高檢測準確性,減少誤報。
- 智慧CC識別:EdgeOne利用多年經驗,透過分析網路流量和多種引數,自動識別並定位攻擊源,有效對抗CC攻擊。
- BOT防護:EdgeOne透過協議、IP和會話特徵識別BOT,結合資料分析建立模型,防止惡意爬蟲攻擊,減少對正常爬蟲的誤傷。
- 攻擊溯源:EdgeOne可以捕獲並分析異常事件,提取攻擊資訊,提供監控頁面展示攻擊詳情,幫助使用者調整防護策略。
- 全天候監測:騰訊安全團隊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作畫等應用環境。
這個話題不需要詳細討論了,我已經整理了開發中需要了解的主要優點和相關文件,以便大家能夠快速入手開發:
- 快速部署:建立高效能應用服務例項,選擇合適的地域、算力方案,並設定例項名稱和硬碟空間。官方文件:https://cloud.tencent.com/document/product/1721/101036
- 生成API文件:在已部署的Stable Diffusion應用基礎上,透過JupyterLab部署API服務。官方文件:https://cloud.tencent.com/document/product/1721/102198
- 模型下載地址:https://civitai.com/
- 匯入外部模型:在騰訊雲高效能應用服務HAI上,可以透過三種方式匯入外部模型,但是為了花錢少一點,我推薦你選擇COS儲存。官方文件:https://cloud.tencent.com/document/product/1721/102523#1bb64c7b-0bc8-4a2a-86f0-ad0e71d8f059
聯動開發
現在讓我們來探討一下為何可以採用聯動開發的方式。我們的目標是以最為經濟實惠的價格部署我們的AI繪畫應用。
- HAI高效能伺服器是按照小時收費的,所以我們很多情況下會選擇關機,但是一旦重啟伺服器,外網IP就會變動,導致你的訪問路徑也就隨之變換。但是這一步可以在EdgeOne中使用邊緣函式解決。
- EdgeOne本身就已經對我們的伺服器做了相應的安全防護。
- HAI高效能伺服器,他是一個Ubuntu20.04環境的虛擬機器,也就是說,我們已經有了一個伺服器,只不過這個伺服器被包裝成了繪畫AI應用,所以不要浪費掉。用起來
我們接下來看下我們會用到哪些技術:HTML、Javascript、CSS、Nginx、Python、邊緣函式中的各個API、Linux基本命令。
準備環境
HAI高效能伺服器
關於建立伺服器的步驟,我就不再進行演示了,請直接檢視官方教程,相關地址也已經提供。我們將重點演示以下內容:下載/上傳模型、透過WEB UI演示AI繪畫功能、下載並配置Nginx以監聽80埠、啟動API介面服務。
下載/上傳模型
這一步我當時使用過如何官方預設的JupyterLab頁面上傳,速度是真的很可觀~我直接放棄了。選擇了COS物件儲存。我們先去下載好看的模型。這裡以https://civitai.com/ 為例。
模型下載到本地之後,去建立COS物件儲存服務。
如何建立這麼簡單的教程,我就不演示了,我們直接看上傳和下載。這裡建立儲存桶的時候,所屬地域一定要和你HAI繪畫伺服器在一起。否則會產生很多額外的花銷。謹記~
進入儲存桶之後,直接點選頁面的上傳檔案即可,請選擇剛才下載的模型檔案。上傳的過程中不要動瀏覽器,一旦重新整理和關閉瀏覽器就會前功盡棄。
稍等片刻,然後點選模型檔案的詳情,進入基本資訊頁面。在這裡,請確保將許可權設定為公有讀取,否則HAI伺服器將無法下載到模型檔案。然後,複製臨時連結。
我們進入已經建立好的HAI伺服器。
進入終端,使用一下幾個命令,下載模型檔案到模型資料夾。
cd stable-diffusion-webui/models/Stable-diffusion
wget 臨時連結貼上到此處
等待下載完畢。
WEB UI演示AI繪畫
讓我們前往WEB UI頁面,嘗試一下看看是否成功安裝了。
在WEB UI頁面上,選擇您自己的模型檔案,並使用一段神奇的提示詞。當然,提示詞越好,生成的結果也會越好。如果有任何不清楚的地方,您可以參考您下載的模型附帶的教程。這樣基本上就沒有問題了。接下來,讓我們再來看看如何讓HAI提供站點服務。
Nginx站點配置
依然是同樣的步驟,在JupyterLab中的終端中直接使用以下命令操作:
- 下載Nginx:使用命令
apt install nginx
進行安裝。 - 啟動Nginx:使用命令
service nginx start
啟動服務。
當您直接使用IP地址在瀏覽器中訪問後,如果顯示以下介面,那就表示安裝成功了。
接下來,我們繼續修改Nginx的配置檔案:
使用以下命令編輯配置檔案:
vim /etc/nginx/sites-available/default
然後,在檔案中新增以下配置:
# 配置/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繪畫出來的圖片。
讓我們透過Nginx來訪問一下圖片,以確保可以正常訪問。
在瀏覽器中輸入以下地址進行訪問:http://你的IP/images/1.png
接下來,我們繼續除錯API服務。請啟動HAI繪畫應用的API服務,使用以下命令:
python launch.py --nowebui --xformers --opt-split-attention --listen --port 7862
然後,在瀏覽器中輸入IP地址和埠號,後面加上/docs路徑即可正常訪問路徑了。
讓我們透過工具來測試一下,看看能否透過Nginx正常訪問介面。你可以隨意選擇一個介面路徑進行測試。我選擇了一個簡單的無請求引數的GET請求作為示例。通常情況下,這個請求會帶有埠號。
使用除錯工具來呼叫一下Nginx配置的路徑,確保一切正常。
如果您還不確定的話,可以透過檢視預設的Nginx訪問路徑來確認。您可以使用以下命令來實時檢視日誌:
tail -f /var/log/nginx/access.log
這樣您就可以確定API是否能夠被正確命中。
啟動API介面服務
為了解決Nginx每次關機後無法正常啟動的難題,我們採取了一項巧妙的解決方案:將啟動命令直接寫入API啟動服務介面中,從而確保服務能夠在每次啟動時正確配置Nginx。具體操作如下:首先,我們需要找到專案中的launch.py檔案。一旦找到了該檔案,我們只需在其中新增幾行命令即可。這些命令將確保Nginx在伺服器啟動時得到正確的配置,並能夠順利啟動。這種方法簡單而高效,能夠有效解決Nginx啟動問題,保證服務的穩定性和可靠性。
程式碼如下:
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。
透過以上步驟,我們的HAI高效能伺服器已經完成了基本配置。無論進行多少次關機重啟,我們的Nginx都會隨著API介面一起啟動,確保了系統的穩定性和可靠性。現在,讓我們繼續深入探討EdgeOne的配置過程。
EdgeOne邊緣函式
在配置EdgeOne時,首先需要確保完成無域名站點的配置,你可以直接參考官方文件進行操作。接著,我們需要建立邊緣函式來實現相應的快取處理。考慮到你沒有企業版許可權,因此無法使用四層加速,但可以透過邊緣函式來達到類似的效果。關於邊緣函式中程式碼的具體使用問題,我建議你仔細閱讀官方文件,其中包含了詳細的操作指南和示例程式碼,可以幫助你快速上手。你可以點選以下連結檢視官方文件:EdgeOne 官方文件
在配置邊緣函式時,你可以選擇Hello World模板作為起點,然後依次點選下一步進行配置。雖然選擇了Hello World模板,但它只是一個起點,我們將在此基礎上進行深入的定製,以實現我們所需要的功能。
完成所有配置後,系統會為你生成一個預設的域名。接下來,我們需要編寫程式碼來實現功能。通常情況下,我會額外編寫一個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));
});
這段程式碼的主要功能是用於處理網路請求並管理資源的快取策略,確保快速響應使用者請求的同時,減少不必要的網路流量。
fetchJquery
函式:- 該函式用於處理當資源未被快取或快取過期時的情況。
- 使用
fetch
函式發起網路請求,獲取資源的最新響應。 - 在響應頭中新增
Cache-Control
,設定資源在服務端快取中的最長有效時間為600秒。 - 使用
cache.put
方法將獲取的響應克隆一份並存入快取中,以便後續請求可以直接從快取中獲取。 - 設定響應頭
x-edgefunctions-cache
為miss
,表示這是一個未命中快取的請求。 - 返回原始響應給請求者。
handleEvent
函式:- 該函式用於檢查請求的URL是否指向一個圖片資源(透過副檔名判斷)。
- 如果請求的不是圖片資源,將返回一個錯誤響應。
- 如果是圖片資源,嘗試從快取中獲取請求的資源。
- 如果快取中存在資源,則設定響應頭
x-edgefunctions-cache
為hit
,表示這是一個命中快取的請求,並返回快取中的資源。 - 如果快取中不存在資源或發生異常(如快取過期),則刪除快取並重新呼叫
fetchJquery
函式獲取資源。
在訪問圖片資源時,需要特別注意的是,我採用了路徑後面的 IP 引數進行訪問。這是因為 HAI 每次重啟後外網 IP 都會發生變化,如果每次都要修改每個邊緣函式的話,會相當繁瑣。為了避免這種情況,我選擇透過引數傳遞的方式來獲取圖片資源。這樣一來,獲取圖片資源的邊緣函式就不需要再進行任何修改了。另外,你還需要對匹配觸發規則進行配置,比如我設定的規則是包含 "/images" 的路徑才會觸發相應的操作。
在瀏覽器中輸入預設域名,並隨意新增 "/images/*.png" 即可訪問相應圖片,前提是該圖片儲存於我們的伺服器上。現在讓我們來觀察一下效果。值得注意的是,這種方式的命中率並不是很高,所以在剛部署完邊緣函式後,需要多重新整理幾次才能看到效果。
靜態網頁
這個邊緣函式專門用於處理使用者互動。
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程式碼。主要功能和組成部分如下:
- HTML結構:
- 頁面包含一個容器
container
,用於佈局兩個主要部分:繪圖結果展示和文字描述輸入。 - 繪圖結果部分包含一個標題和一個影像容器
image-container
,用於展示AI生成的影像。 - 文字描述部分包含兩個文字區域
text-description
和text-description2
,供使用者輸入正向描述和反向描述,用於指導AI繪圖。 - 一個按鈕
generate-button
,用於觸發AI繪圖過程。
- 頁面包含一個容器
- JavaScript邏輯:
- 頁面載入完成後,呼叫
createImageList
函式,動態生成一個圖片列表並展示在頁面上。 - 監聽
generate-button
按鈕的點選事件,收集使用者輸入的描述和選擇的引數,構造一個AI繪圖請求,併傳送到伺服器。 - 使用
XMLHttpRequest
物件傳送POST請求到指定的伺服器地址,處理響應並動態更新頁面上的影像容器以展示生成的影像。
- 頁面載入完成後,呼叫
我們看一下最終效果:
我已經將 IP 地址單獨抽離出來,以確保最小化變動量。如果需要重啟,只需更新 IP 地址,然後重新部署即可重新訪問。
讓我們再次觀察一下生成圖片的效果。點選生成時,後臺介面的響應也都是正常的。
質量確實還有提升空間,我仔細檢查了一下,發現問題可能出在模型的限制上。例如,對於寬高比不能是正方形的影像,可以稍微寬一點或高一點。這樣生成的影像會更具觀賞性。但這些問題都是可以透過除錯解決的。總的來說,我們的EdgeOne和HAI的夢幻聯動已經取得了階段性的成果,算是告一段落了。
總結
本篇文章源於他人創意的啟發與深入思考,我投入了大量時間與精力,致力於EdgeOne與HAI之間的協同除錯,克服了眾多技術奇葩問題。透過不懈的努力,成功地將這兩個強大的平臺整合在一起,打造出一個還不錯的參考案例。這次探索也是我個人首次涉足邊緣函式式產品的實踐之旅。我希望透過分享這次的經歷和成果,能夠激發更多人對這些前沿技術的興趣和探索。如果你對我的專案感興趣,或者從中獲得了靈感與幫助,不妨點贊支援,也歡迎關注我,一起交流學習,共同進步。