HTTP Trigger je jeden z nejvíce používaných Azure Functions triggerů, jelikož je využíván k vytvoření serverless API endpointů. V tomto článku si ho ukážeme od založení, po publikaci a při tom si vytvoříme i aplikaci, co má reálné využití!
Co tedy budeme dělat, ptáte se? No, vytvoříme si jednoduchý endpoint na Azure Functions za pomocí HTTP Triggeru, který nám přemění obrázek na ASCII art. V jednoduchosti na tento endpoint zašle uživatel Base64 text, ze kterého se poté pošle zpět ASCII art. Pokud nevíte, co ASCII art je, tak zde je jeden příklad, který náš kód vytvořil:
Obsah
Základ projektu
Pokud by se vám něco v následujících krocích nepovedlo zprovoznit, tak se na celý projekt můžete kouknout na Githubu.
Jelikož už jsme si dopodrobna popsali proces založení Azure Functions, tak sem pouze přidám list věcí, co musíte před programováním založit. Pokud nevíte, jak něco z výše uvedených udělat, tak si prosím přečtěte první část této série, kde se všechny potřebné informace dozvíte.
- Založený Azure Functions prostředek na Azure
- Nastavené vývojové prostředí (node, func, az)
Dále, abychom pořád neopakovali jeden a ten samý proces, tak tento projekt vyvineme v Pythonu. Co to pro vás znamená? No, jenom to, že si musíte na Azure vybrat, že chcete, aby se projekt psal v Pythonu a také po příkazu func init musíte vybrat Python. Nic jiného se však nemění. V tom je krása Azure Functions, jakmile je máte nastavené, tak to jde psát bez problémů ve většině populárních jazyků. Dále si poté přes func new přidáte nový HTTP Trigger. Tento trigger poté používáme na převedení obrázků na ASCII art.
Po splnění kroků výše, by struktura vašeho vývojového souboru měla vypadat následovně:
Všimněte si, jak se struktura tohoto projektu liší od projektu v Node.js. Pokud vás zajímá, co každý soubor v projektu dělá, tak si můžete přečíst getting_started.md. Pro nás ale budou hlavní soubory requirements.txt a __init__.py. V souboru requirements.txt jsou sepsány všechny balíčky, které budeme v programu používat. Samotný program poté píšeme do __init__.py, který by měl vypadat nějak takto:
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
Vytváření funkcionality
Jako první krok, musíme soubor trošičku pročistit, abychom odstranili „Hello World“ funkcionalitu.
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
# Logni start funkce
logging.info('Python HTTP endpoint triggered.')
# Pošli zpět odpověď serveru
return func.HttpResponse(
"This HTTP triggered function executed successfully.",
status_code=200
)
Tak, teď máme čistou aplikaci, která pouze vrací HTTP kód 200 a zprávu. Vzhledem k tomu, že naše aplikace získá velké množství dat na request, nastavíme funkci pouze na POST requesty. To uděláme tak, že půjdeme do functions.json a zde změníme pole methods na [„post“].
Teď už musíme jenom naprogramovat konvertování obrázků na ASCII art.
První věc, kterou potřebujeme, je získání obrázku. Proto si pod logování startu funkce přidáme vyžádání obrázku v těle requestu.
# Získej parametr obrazek.
# Pokus se vzít z těla requestu.
obrazek = {}
try:
req_body = req.get_json()
except ValueError:
pass
else:
obrazek = req_body.get('obrazek')
# Pokud ani po kontrolach neni obrazek nadefinovan, pošli zpět 422
if not obrazek:
return func.HttpResponse(
"Prosím, zadejte base64 'obrazek' argument",
status_code=422
)
Dále, vzhledem k tomu, že potřebujeme nějaké knihovny na zpracování obrázku, přidáme si je do projektu. K tomu nám poslouží soubor requirements.txt. V tomto souboru byste měli mít defaultně přidán package azure-functions. Na náš projekt potřebujeme následující knihovny: PIL, io, base64. Váš soubor by poté měl vypadat nějak takto:
# Do not include azure-functions-worker as it may conflict with the Azure Functions platform
azure-functions
PIL
io
base64
Jakmile máme balíčky přidané, tak je můžeme v kódu použít, proto si naimportujeme tyto knihovny v horní části souboru.
# Importy pro azure
import logging
import azure.functions as func
# Importy pro práci s obrázkem
from PIL import Image
from io import BytesIO
import base64
Po dlouhém nastavování a importování se už konečně můžeme pustit na úpravu samotného obrázku. Jako první musíme obrázek převést z base64 na Image. Dále upravíme rozlišení obrázku, jelikož nechceme, aby byl náš ASCII art obrovský. Jako poslední věc, co uděláme, je to, že si obrázek převedeme na černobílý. To děláme z toho důvodu, abychom poté mohli každému pixelu přiřadit správnou hodnotu. To vše můžeme vidět v následujícím kódu:
# Získej obrázek z base64 formátu
img = Image.open(BytesIO(base64.b64decode(obrazek)))
# Změň šířku a výšku obrázku
new_width = 180
width, height = img.size
aspect_ratio = height/width
new_height = aspect_ratio * new_width * 0.55
img = img.resize((new_width, int(new_height)))
# Přehoď obrázek na černobílý
img = img.convert('L')
Jakmile máme obrázek zpracovaný, tak už pouze stačí jít pixel po pixelu, vzít hodnotu, přiřadit jí k charakteru. Na to, abychom toto mohli provést, tak potřebujeme pár věcí. Zaprvé, pole charakterů, které poté budeme dosazovat a z kterého budeme obrázek tvořit. Poté musíme jít pixel po pixelu a přiřadit tyto charaktery. Jako poslední část je poskládání ASCII art dohromady. To by měl udělat tento kus kódu:
chars = ["@", "#", "$", "%", "?", "*", "+", ";", ":", ",", "."]
# Získej pixely z obrázku
pixels = img.getdata()
# Nahraď každý pixel s chrakterem z pole
new_pixels = [chars[pixel//25] for pixel in pixels]
new_pixels = ''.join(new_pixels)
# Vytvoř ASCII art
new_pixels_count = len(new_pixels)
ascii_image = [new_pixels[index:index + new_width] for index in range(0, new_pixels_count, new_width)]
ascii_image = "\n".join(ascii_image)
# Logni finální obrázek
logging.info(ascii_image)
# Pošli zpět ASCII art
return func.HttpResponse(
ascii_image,
status_code=200
)
Finální funkce by měla vypadat takto:
# Importy pro azure
import logging
import azure.functions as func
# Importy pro práci s obrázkem
from PIL import Image
from io import BytesIO
import base64
# Nastavení konstant
new_width = 180
chars = ["@", "#", "$", "%", "?", "*", "+", ";", ":", ",", "."]
def main(req: func.HttpRequest) -> func.HttpResponse:
# Logni start funkce
logging.info('Python HTTP endpoint triggered.')
# Získej parametr obrazek.
# Pokus se vzít z těla requestu.
obrazek = {}
try:
req_body = req.get_json()
except ValueError:
pass
else:
obrazek = req_body.get('obrazek')
# Pokud ani po kontrolach neni obrazek nadefinovan, pošli zpět 422
if not obrazek:
return func.HttpResponse(
"Prosím, zadejte base64 'obrazek' argument",
status_code=422
)
# Získej obrázek z base64 formátu
img = Image.open(BytesIO(base64.b64decode(obrazek)))
# Změň šířku a výšku obrázku
width, height = img.size
aspect_ratio = height/width
new_height = aspect_ratio * new_width * 0.55
img = img.resize((new_width, int(new_height)))
# Přehoď obrázek na černobílý
img = img.convert('L')
# Získej pixely z obrázku
pixels = img.getdata()
# Nahraď každý pixel s chrakterem z pole
new_pixels = [chars[pixel//25] for pixel in pixels]
new_pixels = ''.join(new_pixels)
# Vytvoř ASCII art
new_pixels_count = len(new_pixels)
ascii_image = [new_pixels[index:index + new_width] for index in range(0, new_pixels_count, new_width)]
ascii_image = "\n".join(ascii_image)
# Logni finální obrázek
logging.info(ascii_image)
# Pošli zpět ASCII art
return func.HttpResponse(
ascii_image,
status_code=200
)
Testování programu
Jelikož naše funkce přijímá pouze POST request, tak musíme napsat kód na její vyzkoušení. Vzhledem k tomu, že se celý článek táhne v duchu Pythonu, tak si kód na otestování rovnou napíšeme v něm. Na to však potřebujete už mít nainstalovaný Python!
Abychom si to zrekapitulovali, máme endpoint, který přijímá POST requesty a vyžaduje v těle requestu json s proměnnou obrazek. Tato proměnná musí být base64 data. Jakmile toto odešleme, vrátí se nám pouze text, který už je hotový ASCII art. Tento ASCII art poté chceme uložit do txt souboru, aby se nám to hezky zobrazovalo.
# Musíte si doinstalovat přes: pip install <název>
import requests
import json
# Nadefinujeme si endpoint na vyzkoušení
url = "http://localhost:7071/api/HttpTrigger"
# Nadefinujeme si tělo requestu
payload = json.dumps({
"obrazek": "<Vložte base64>"
})
# Zašleme hlavičku, že se jedná o json
headers = {
'Content-Type': 'application/json'
}
# Získáme odpověď od serveru
response = requests.request("POST", url, headers=headers, data=payload)
with open("ASCII_art.txt", "w") as text_file:
text_file.write(response.text)
Závěr
Tak a jsme u konce. Vím, že to nebylo úplně nejlehčí, ale zároveň si myslím, že se to dá zvládnout. Na závěr bych také chtěl dát odkaz na skvělý tutoriál, jak udělat z obrázku ASCII art, kde se můžete dozvědět více technických informací o tom, jak se pracuje v Pythonu s pixely. Dále bych se také rád odkázal na Azure Functions Http Trigger dokumentaci, která mi v nastavování tohoto projektu hodně pomohla.
Pokud se vám článek líbil, neváhejte a přečtěte si pokračování této série.