FastAPI와 JS에서 통신하기
ComfyUI에서 서버를 실행 중 (로컬 환경)
ComfyUI와 통신하는 service.py 서버 실행중 (로컬 환경)
client에서 service.py와 fetch async를 통해 통신
그러나 통신 중 결과를 받지 못하고 Failed to Fetch 오류가 발생
from fastapi import FastAPI, HTTPException, File, UploadFile
from pydantic import BaseModel
import json
from urllib import request
import asyncio
from fastapi.middleware.cors import CORSMiddleware
import os
from fastapi import FastAPI, Request
import logging
app = FastAPI()
# CORS 설정
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 모든 출처 허용
allow_credentials=True,
allow_methods=["*"], # 모든 HTTP 메소드 허용
allow_headers=["*"], # 모든 헤더 허용
)
COMFYUI_URL = "127.0.0.1:8188" # ComfyUI 서버 IP 및 포트
# 요청 데이터 모델 정의
class WorkflowRequest(BaseModel):
workflow: dict
# 응답 데이터 모델 정의
class WorkflowResponse(BaseModel):
image: str # base64 인코딩된 이미지 데이터
def queue_prompt(prompt_workflow, ip):
p = {"prompt": prompt_workflow}
data = json.dumps(p).encode('utf-8')
req = request.Request(f"http://{ip}/prompt", data=data)
try:
res = request.urlopen(req)
if res.code != 200:
raise Exception(f"Error: {res.code} {res.reason}")
return json.loads(res.read().decode('utf-8'))['prompt_id']
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
async def check_progress(prompt_id: str, ip: str):
while True:
try:
req = request.Request(f"http://{ip}/history/{prompt_id}")
res = request.urlopen(req)
if res.code == 200:
history = json.loads(res.read().decode('utf-8'))
if prompt_id in history:
return history[prompt_id]
except Exception as e:
print(f"Error checking progress: {str(e)}")
await asyncio.sleep(1) # 1초 대기
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
@app.post("/workflow/cloth")
async def cloth(img_model: UploadFile = File(...), img_product: UploadFile = File(...)):
try:
# 이미지를 저장할 경로 설정
for img in [img_model, img_product]:
image_path = f"./ComfyUI/input/{img.filename}"
with open(image_path, "wb") as f:
f.write(await img.read())
# 저장된 이미지 경로를 워크플로우에 포함
with (open("./workflow/cloth_api.json", "r", encoding="utf-8")) as f:
workflow_request = WorkflowRequest(workflow=json.loads(f.read()))
# 이미지 경로를 워크플로우에 추가
workflow_request.workflow["3"]["inputs"]["image"] = img_model.filename
workflow_request.workflow["4"]["inputs"]["image"] = img_product.filename
# ComfyUI에 워크플로우 전송
prompt_id = queue_prompt(workflow_request.workflow, COMFYUI_URL)
result = await check_progress(prompt_id, COMFYUI_URL)
# 결과에서 마지막 이미지 URL 추출
final_image_url = None
for node_id, node_output in result['outputs'].items():
if 'images' in node_output:
for image in node_output['images']:
final_image_url = f"http://{COMFYUI_URL}/view?filename={image['filename']}&type=temp"
# 반환할 이미지 URL
if final_image_url:
return {"status": "completed", "images": final_image_url}
else:
return {"status": "completed", "images": None}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Uvicorn 실행
import uvicorn
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
async function generateImage() {
const selectedImage1 = document.getElementById('selectedImage1');
const selectedImage2 = document.getElementById('selectedImage2');
const ballContainer = document.querySelector('.ball-container');
const imagePreviewDiv = document.querySelector('.image-preview');
const imagePlaceholder = document.getElementById('image-placeholder');
const loadingText = document.createElement('p');
loadingText.id = 'loading-text';
loadingText.innerText = '이미지 생성중...';
loadingText.style.marginTop = '-30px';
if (!selectedFromTab1 || !selectedFromTab2) {
alert('상품 이미지와 연예인 이미지를 모두 선택해주세요.');
return;
}
try {
imagePlaceholder.style.display = 'none';
ballContainer.style.display = 'grid';
ballContainer.insertAdjacentElement('afterend', loadingText);
const formData = new FormData();
const modelImage = await fetch(selectedImage2.querySelector('img').src).then(res => res.blob());
const productImage = await fetch(selectedImage1.querySelector('img').src).then(res => res.blob());
formData.append("img_model", modelImage, "baekjongwon.jpg"); // Blob 추가
formData.append("img_product", productImage, "img-cloth.jpg"); // Blob 추가
const response = await fetch(COMFYUI_SERVER_URL + "workflow/cloth", {
method: 'POST',
headers: {
'Accept' : '*/*'
},
body: formData
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setTimeout(() => {
ballContainer.style.display = 'none';
loadingText.remove();
if (data.status === "completed" && data.images) {
const resultImg = document.createElement('img');
resultImg.src = data.images;
resultImg.alt = "Generated Image";
imagePreviewDiv.innerHTML = '';
imagePreviewDiv.appendChild(resultImg);
} else {
throw new Error('Image generation failed');
}
}, 5000);
} catch (error) {
console.error('Error:', error);
alert(`오류가 발생했습니다: ${error.message}`);
ballContainer.style.display = 'none';
loadingText.remove();
imagePlaceholder.style.display = 'block';
}
}
python -m http.server를 실행하여 환경을 분리하여 문제 해결