Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Ovladač mssql-python podporuje binary, varbinary, a image datové typy pro Microsoft SQL.
Microsoft SQL ukládá binární data do těchto typů sloupců:
| Typ | Popis | Maximální velikost |
|---|---|---|
binary(n) |
Binární data s pevnou délkou. | 8 000 bajtů |
varbinary(n) |
Binární data s proměnnou délkou | 8 000 bajtů |
varbinary(max) |
Velká binární data. | 2 GB |
image |
Původní velká binární data (zastaralá). | 2 GB |
Ovladač mssql-python vrací binární data jako Python bytes objekty.
Kdy uložit binární soubor do databáze versus do souborového systému:
- Ukládejte do databáze, když jsou soubory malé (pod 1 MB), transakční konzistence s ostatními daty je důležitá, nebo když potřebujete zálohovat data a soubory dohromady.
- Ukládejte do souborového systému (nebo Azure Blob Storage), když jsou soubory velké (přes 1 MB), potřebujete doručení přes CDN, nebo když potřebujete doručovat soubory přímo klientům bez návratu do databáze.
- Pro kompromis je funkce FILESTREAM od Microsoft SQL, která ukládá data do souborového systému s transakční konzistencí.
Vložte binární data
Předávejte objekty jazyka Python bytes jako parametry; ovladač je mapuje na sloupce varbinary.
Vložte bajty přímo
Vytvořte Python bytes objekt a předejte ho jako dotazovací parametr:
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Create a table to hold binary documents
cursor.execute("""
CREATE TABLE #Documents (
ID INT IDENTITY PRIMARY KEY,
Name NVARCHAR(255),
Content VARBINARY(MAX),
Size INT NULL,
ContentHash VARBINARY(32) NULL
)
""")
# Binary data as bytes
binary_data = b'\x00\x01\x02\x03\x04\x05'
cursor.execute(
"INSERT INTO #Documents (Name, Content) VALUES (%(name)s, %(content)s)",
{"name": "sample.bin", "content": binary_data}
)
conn.commit()
Vložit ze souboru
Přečtěte soubor v binárním režimu a vložte jeho obsah:
def insert_file(cursor, file_path: str, name: str):
"""Insert a file as binary data."""
with open(file_path, "rb") as f:
content = f.read()
cursor.execute(
"INSERT INTO #Documents (Name, Content, Size) VALUES (%(name)s, %(content)s, %(size)s)",
{"name": name, "content": content, "size": len(content)}
)
insert_file(cursor, "image.png", "profile_picture.png")
conn.commit()
Vložit obrazový soubor
Ukládejte obrazové soubory s metadaty detekcí typů MIME z přípon souboru:
# Create a table to hold images
cursor.execute("""
CREATE TABLE #Images (
ID INT IDENTITY PRIMARY KEY,
FileName NVARCHAR(255),
FileSize INT NULL,
ContentType NVARCHAR(100) NULL,
Width INT NULL,
Height INT NULL,
ImageData VARBINARY(MAX),
Description NVARCHAR(MAX) NULL
)
""")
def insert_image(cursor, image_path: str, description: str):
"""Insert an image into the database."""
import os
with open(image_path, "rb") as f:
image_data = f.read()
cursor.execute("""
INSERT INTO #Images (FileName, FileSize, ContentType, ImageData, Description)
VALUES (%(filename)s, %(filesize)s, %(content_type)s, %(data)s, %(desc)s)
""", {
"filename": os.path.basename(image_path),
"filesize": len(image_data),
"content_type": get_content_type(image_path),
"data": image_data,
"desc": description
})
def get_content_type(path: str) -> str:
"""Determine MIME type from file extension."""
ext = path.lower().split(".")[-1]
types = {
"png": "image/png",
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"gif": "image/gif",
"pdf": "application/pdf",
}
return types.get(ext, "application/octet-stream")
Načtení binárních dat
Ovladač vrací varbinární a binární sloupcové hodnoty jako objekty v Pythonbytes.
Sloupec pro načtení binárních souborů
Dotazujte binární sloupce a zkontrolujte vrácený bytes objekt:
cursor.execute(
"SELECT LargePhotoFileName, LargePhoto FROM Production.ProductPhoto WHERE ProductPhotoID = %(id)s",
{"id": 70}
)
row = cursor.fetchone()
# LargePhoto is bytes
print(type(row.LargePhoto)) # <class 'bytes'>
print(len(row.LargePhoto)) # Number of bytes
Uložení do souboru
Získejte binární data a zapíšte je na disk:
def save_photo(cursor, photo_id: int, output_path: str):
"""Retrieve binary data and save to file."""
cursor.execute(
"SELECT LargePhoto FROM Production.ProductPhoto WHERE ProductPhotoID = %(id)s",
{"id": photo_id}
)
row = cursor.fetchone()
if row and row.LargePhoto:
with open(output_path, "wb") as f:
f.write(row.LargePhoto)
print(f"Saved {len(row.LargePhoto)} bytes to {output_path}")
else:
print("Photo not found or empty")
save_photo(cursor, 70, "output.gif")
Export binárních řádků do souborů
Exportujte více binárních řádků do lokálních souborů:
import os
def export_photos(cursor, output_dir: str):
"""Export product photos to a directory."""
os.makedirs(output_dir, exist_ok=True)
cursor.execute("""
SELECT ProductPhotoID, LargePhotoFileName, LargePhoto
FROM Production.ProductPhoto
WHERE ProductPhotoID > 1
""")
count = 0
for row in cursor:
output_path = os.path.join(output_dir, f"{row.ProductPhotoID}_{row.LargePhotoFileName}")
with open(output_path, "wb") as f:
f.write(row.LargePhoto)
count += 1
print(f"Exported {count} photos to {output_dir}")
export_photos(cursor, "./photos")
Vložit binární hodnoty NULL
Předejte None, chcete-li vložit SQL NULL do binárního sloupce. Protože #Documents je dočasná tabulka, nejprve deklarujte typy parametrů pomocí setinputsizes(), aby ovladač navázal Content jako varbinary:
cursor.setinputsizes([(mssql_python.SQL_WVARCHAR, 255, 0), (mssql_python.SQL_VARBINARY, 0, 0)])
cursor.execute(
"INSERT INTO #Documents (Name, Content) VALUES (%(name)s, %(content)s)",
{"name": "empty", "content": None}
)
Note
Ovladač obvykle odvodí typy parametrů pomocí SQLDescribeParam, ale nemůže vyřešit metadata typů pro dočasnou tabulku nebo proměnnou tabulky. Vložení None do sloupce binary nebo varbinary dočasného objektu bez setinputsizes() vyvolá ProgrammingError: Implicit conversion from data type varchar to varbinary(max) is not allowed. Předejte jeden záznam na parametr v pořadí a použijte SQL typovou konstantu, například mssql_python.SQL_VARBINARY pro binární sloupec. U běžné (trvalé) tabulky ovladač automaticky určí typ a můžete předat None přímo.
Velká binární data
U souborů nad pár megabajtů vložte celý obsah do jednoho varbinárního (max) zápisu místo čtení souboru po malých částech.
Streamovat velké soubory
U velkých souborů zpracováváme v částech:
def insert_large_file(conn, table: str, file_path: str, chunk_size: int = 8192):
"""Insert large file in chunks using updatetext-style approach."""
# Validate table name to prevent SQL injection
import re
if not re.match(r'^#{0,2}[A-Za-z_][A-Za-z0-9_.]*$', table):
raise ValueError(f"Invalid table name: {table}")
cursor = conn.cursor()
# Get file size
import os
file_size = os.path.getsize(file_path)
# Insert initial row with empty binary
cursor.execute(f"""
INSERT INTO {table} (Name, Content, Size)
VALUES (%(name)s, 0x, %(size)s);
SELECT SCOPE_IDENTITY();
""", {"name": os.path.basename(file_path), "size": file_size})
row_id = cursor.fetchval()
# For modern Microsoft SQL, better to use varbinary(max) and single insert
# This example shows streaming approach
with open(file_path, "rb") as f:
content = f.read()
cursor.execute(f"""
UPDATE {table} SET Content = %(content)s WHERE ID = %(id)s
""", {"content": content, "id": row_id})
conn.commit()
return row_id
Použijte hromadnou kopii pro binární data
Použijte tuto metodu bulkcopy() k efektivnímu vložení více binárních souborů v jedné operaci.
Important
bulkcopy() data se načítají přes samostatné připojení k serveru, takže cílová tabulka musí již existovat a být commitovaná a viditelná pro ostatní relace. Pokud tabulku vytvoříte ve stejném skriptu s vypnutým automatickým commitem, zavolejte conn.commit() před bulkcopy(). Lokální dočasné tabulky (#name) nejsou podporovány, protože jsou soukromé pro relaci, která je vytvořila. Nepotvrzený nebo nedosažitelný cíl způsobí selhání bulkcopy() s vypršením časového limitu Failed to retrieve destination metadata.
Ve výchozím nastavení mapuje bulkcopy() každou hodnotu v řádku ke sloupci tabulky podle pořadí, takže každý sloupec musí mít hodnotu ve správném pořadí. Když má tabulka sloupec identity nebo vyplníte jen některé sloupce, přejděte column_mappings a explicitně pojmenujte cílové sloupce. Jinak se hodnoty přesunou do špatných sloupců a bulkcopy() selžou.
def bulk_insert_files(conn, table: str, file_paths: list[str]):
"""Bulk insert multiple binary files."""
import os
data = []
for path in file_paths:
with open(path, "rb") as f:
content = f.read()
data.append((os.path.basename(path), content, len(content)))
cursor = conn.cursor()
result = cursor.bulkcopy(table, data, column_mappings=["Name", "Content", "Size"])
conn.commit()
return result["rows_copied"]
Binární datové operace
Použijte funkci Microsoft SQL HASHBYTES k porovnání binárního obsahu bez načítání plných hodnot.
Porovnejte binární data
Najděte binární řádky, které sdílejí identický obsah, výpočtem a porovnáním hashů obsahu v Microsoft SQL Server:
# Find rows with identical binary content by hash
cursor.execute("""
SELECT LargePhotoFileName, HASHBYTES('SHA2_256', LargePhoto) AS PhotoHash
FROM Production.ProductPhoto
WHERE ProductPhotoID > 1
""")
hashes = {}
for row in cursor:
hash_value = row.PhotoHash # bytes
if hash_value in hashes:
print(f"Duplicate: {row.LargePhotoFileName} matches {hashes[hash_value]}")
else:
hashes[hash_value] = row.LargePhotoFileName
Výpočet hashe v Pythonu
Vypočítejte SHA-256 hashe v Python a uložte je spolu s binárními daty:
import hashlib
def insert_with_hash(cursor, name: str, content: bytes):
"""Insert binary data with computed hash."""
content_hash = hashlib.sha256(content).digest()
cursor.execute("""
INSERT INTO #Documents (Name, Content, ContentHash)
VALUES (%(name)s, %(content)s, %(hash)s)
""", {"name": name, "content": content, "hash": content_hash})
Kódování a dekódování binárních dat
Převádějte binární data na a z řetězců kódovaných base64:
import base64
# Store base64-encoded string
def insert_base64(cursor, name: str, base64_data: str):
"""Insert base64-encoded data as binary."""
binary_data = base64.b64decode(base64_data)
cursor.execute(
"INSERT INTO #Documents (Name, Content) VALUES (%(name)s, %(content)s)",
{"name": name, "content": binary_data}
)
# Retrieve as base64
def get_as_base64(cursor, doc_id: int) -> str:
"""Retrieve binary data as base64 string."""
cursor.execute("SELECT Content FROM #Documents WHERE ID = %(id)s", {"id": doc_id})
row = cursor.fetchone()
return base64.b64encode(row.Content).decode("utf-8")
Práce s konkrétními binárními formáty
Tyto příklady ukazují, jak ověřit a vložit běžné formáty souborů před jejich uložením.
Dokumenty PDF
Před vložením ověřte podpisy PDF souborů:
def insert_pdf(cursor, pdf_path: str, title: str):
"""Insert a PDF document."""
with open(pdf_path, "rb") as f:
pdf_data = f.read()
# Verify it's a PDF (magic bytes)
if not pdf_data.startswith(b'%PDF'):
raise ValueError("Not a valid PDF file")
cursor.execute(
"INSERT INTO #Documents (Name, Content) VALUES (%(title)s, %(data)s)",
{"title": title, "data": pdf_data}
)
Obrázky s PIL/Pillow
Upravte velikost obrázků pomocí Pillow (PIL) před uložením, abyste ušetřili místo:
from PIL import Image
import io
import os
def insert_resized_image(cursor, image_path: str, max_size: tuple = (800, 600)):
"""Insert a resized image."""
# Open and resize
img = Image.open(image_path)
img.thumbnail(max_size, Image.LANCZOS)
# Convert to bytes
buffer = io.BytesIO()
img.save(buffer, format=img.format or "PNG")
image_bytes = buffer.getvalue()
cursor.execute("""
INSERT INTO #Images (FileName, Width, Height, ImageData)
VALUES (%(name)s, %(width)s, %(height)s, %(data)s)
""", {
"name": os.path.basename(image_path),
"width": img.width,
"height": img.height,
"data": image_bytes
})
def get_image_as_pil(cursor, image_id: int) -> Image.Image:
"""Retrieve image as PIL Image object."""
cursor.execute("SELECT ImageData FROM #Images WHERE ID = %(id)s", {"id": image_id})
row = cursor.fetchone()
return Image.open(io.BytesIO(row.ImageData))
Komprimovaná data
Snižte úložiště kompresí binárních dat pomocí gzip před vložením:
import gzip
def insert_compressed(cursor, name: str, data: bytes):
"""Insert data with gzip compression."""
compressed = gzip.compress(data)
cursor.execute(
"INSERT INTO #Documents (Name, Content, Size) VALUES (%(name)s, %(content)s, %(size)s)",
{"name": name, "content": compressed, "size": len(data)}
)
def get_decompressed(cursor, data_id: int) -> bytes:
"""Retrieve and decompress data."""
cursor.execute(
"SELECT Content FROM #Documents WHERE ID = %(id)s",
{"id": data_id}
)
row = cursor.fetchone()
return gzip.decompress(row.Content)
Osvědčené postupy
Použijte tyto pokyny k výběru správného typu sloupce a strategie ukládání binárních dat.
Používejte vhodné typy sloupců
Vyberte typ sloupce podle charakteristik vašich dat:
Pro různé binární případy užití vyberte příslušný typ dat Microsoft SQL:
-- For small fixed-size binary (for example, hashes, UUIDs)
binary(32) -- SHA-256 hash
-- For variable-size binary up to 8KB (for example, thumbnails, small icons)
varbinary(8000)
-- For large binary data (for example, documents, images)
varbinary(max) -- Up to 2GB
Zvažte FILESTREAM pro velké soubory
U velkých souborů (přes 1 MB) zvažte funkci Microsoft SQL FILESTREAM, která ukládá data do souborového systému. FILESTREAM vyžaduje konfiguraci na straně serveru před použitím:
# FILESTREAM-enabled databases store large binaries more efficiently
# Access is still through normal queries but storage is file-based
large_binary_data = b"\x25\x50\x44\x46" + b"\x00" * 100 # sample data
cursor.execute("""
CREATE TABLE ##FileStreamDemo (Name NVARCHAR(100), Document VARBINARY(MAX))
""")
cursor.execute("""
INSERT INTO ##FileStreamDemo (Name, Document)
VALUES (%(name)s, %(content)s)
""", {"name": "large_doc.pdf", "content": large_binary_data})
cursor.execute("SELECT Name, DATALENGTH(Document) AS DocSize FROM ##FileStreamDemo")
row = cursor.fetchone()
print(f"{row.Name}: {row.DocSize} bytes")
Validace binárních dat
Ověřte typy souborů kontrolou magických bajtů (podpisů souboru) před uložením binárních dat:
def insert_safe_image(cursor, name: str, data: bytes):
"""Insert image with validation."""
# Check file signatures (magic bytes)
signatures = {
b'\x89PNG': 'image/png',
b'\xff\xd8\xff': 'image/jpeg',
b'GIF87a': 'image/gif',
b'GIF89a': 'image/gif',
}
content_type = None
for sig, mime in signatures.items():
if data.startswith(sig):
content_type = mime
break
if content_type is None:
raise ValueError("Unknown or unsupported image format")
cursor.execute("""
INSERT INTO #Images (FileName, ContentType, ImageData)
VALUES (%(name)s, %(type)s, %(data)s)
""", {"name": name, "type": content_type, "data": data})