Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De mssql-python-driver ondersteunt binary, varbinary, en image datatypes voor Microsoft SQL.
Microsoft SQL slaat binaire gegevens op in deze kolomtypen:
| Type | Beschrijving | Maximale grootte |
|---|---|---|
binary(n) |
Binaire data met vaste lengte. | 8.000 bytes |
varbinary(n) |
Binaire gegevens met variabele lengte. | 8.000 bytes |
varbinary(max) |
Grote binaire gegevens. | 2 GB |
image |
Grote verouderde binaire gegevens (verouderd). | 2 GB |
De mssql-python-driver geeft binaire gegevens terug als Python-objectenbytes.
Wanneer moet binair opgeslagen worden in de database versus in het bestandssysteem:
- Sla in de database wanneer bestanden klein zijn (onder 1 MB), transactionele consistentie met andere data belangrijk is, of je data en bestanden samen moet back-uppen.
- Sla op in het bestandssysteem (of Azure Blob Storage) wanneer bestanden groot zijn (meer dan 1 MB), je CDN-levering nodig hebt, of bestanden direct aan clients moet leveren zonder database heen en terug te gaan.
- Als middenweg slaat de FILESTREAM-functie van Microsoft SQL gegevens op in het bestandssysteem met transactionele consistentie.
Binaire gegevens invoegen
Geef Python-objecten bytes als parameters door; de driver koppelt ze aan varbinaire kolommen.
Bytes rechtstreeks invoegen
Maak een Python-object bytes aan en geef het door als queryparameter:
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()
Invoegen uit bestand
Lees een bestand in binaire modus en voeg de inhoud ervan in:
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()
Afbeeldingsbestand invoegen
Sla afbeeldingsbestanden met metadata op door MIME-types te detecteren uit bestandsextensies:
# 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")
Binaire gegevens ophalen
De driver geeft varbinaire en binaire kolomwaarden terug als Python-objectenbytes.
Kolom met binaire gegevens ophalen
Zoek binaire kolommen op en inspecteer het geretourneerde bytes object:
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
Opslaan in bestand
Haal binaire gegevens op en schrijf die naar de schijf:
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")
Exporteer binaire rijen naar bestanden
Exporteer meerdere binaire rijen naar lokale bestanden:
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")
Voeg NULL-binaire waarden in
Geef None door om een SQL NULL-waarde in een binaire kolom in te voegen. Omdat #Documents een tijdelijke tabel is, declareer je de parametertypes door eerst te gebruiken setinputsizes() zodat de driver bindt Content als 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}
)
Opmerking
De driver leidt normaal gesproken parametertypes af via SQLDescribeParam, maar kan geen typemetadata oplossen voor een tijdelijke tabel of tabelvariabele. Invoegen None in een binary of varbinary kolom van een tijdelijke object zonder setinputsizes() verhoogt ProgrammingError: Implicit conversion from data type varchar to varbinary(max) is not allowed. Geef één invoer per parameter in volgorde door en gebruik een SQL-typeconstante zoals mssql_python.SQL_VARBINARY voor de binaire kolom. Voor een gewone (permanente) tabel lost de driver het type automatisch op en kun je direct doorgeven None .
Grote binaire gegevens
Voor bestanden van een paar megabyte voeg je de volledige inhoud in één varbinaire (max) schrijf in plaats van het bestand in kleine stukjes te lezen.
Grote bestanden streamen
Voor grote bestanden, verwerk in chunken:
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
Gebruik bulk copy voor binaire data
Gebruik de bulkcopy() methode om efficiënt meerdere binaire bestanden in één bewerking in te voegen.
Important
bulkcopy() laadt data via een aparte verbinding naar de server, dus de bestemmingstabel moet al bestaan en gecommit en zichtbaar zijn voor andere sessies. Als je de tabel in hetzelfde script aanmaakt met autocommit uitgeschakeld, roep conn.commit() dan vóór bulkcopy(). Lokale tijdelijke tabellen (#name) worden niet ondersteund omdat ze privé zijn voor de sessie die ze heeft aangemaakt. Een niet-bevestigde of onbereikbare bestemming zorgt ervoor dat bulkcopy() mislukt door een Failed to retrieve destination metadata-time-out.
Standaard wijst bulkcopy() elke waarde in een rij op basis van de positionele volgorde toe aan een tabelkolom, dus elke kolom moet in de juiste volgorde een waarde hebben. Wanneer de tabel een identiteitskolom heeft of je slechts enkele kolommen vult, ga dan door column_mappings om de doelkolommen expliciet te benoemen. Anders verschuiven de waarden naar de verkeerde kolommen en bulkcopy() faalt.
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"]
Binaire databewerkingen
Gebruik de HASHBYTES functie van Microsoft SQL om binaire inhoud te vergelijken zonder volledige waarden op te halen.
Vergelijk binaire gegevens
Vind binaire rijen die identieke inhoud delen door contenthashes te berekenen en te vergelijken in 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
Hash berekenen in Python
Bereken SHA-256 hashes in Python en sla ze op naast binaire data:
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})
Codeer en decodeer binaire gegevens
Converteer binaire gegevens naar en van base64-gecodeerde strings:
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")
Werk met specifieke binaire formaten
Deze voorbeelden laten zien hoe je veelvoorkomende bestandsformaten kunt valideren en invoegen voordat je ze opslaat.
PDF-documenten
Controleer PDF-handtekeningen voordat je ze invoegt:
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}
)
Afbeeldingen met PIL/Pillow
Verander afbeeldingen met Pillow (PIL) voordat je ze opslaat om ruimte te besparen:
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))
Gecomprimeerde data
Verminder opslag door binaire gegevens te comprimeren met gzip vóór invoeging:
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)
Beste praktijken
Pas deze richtlijnen toe om het juiste kolomtype en opslagstrategie voor binaire data te kiezen.
Gebruik geschikte kolomtypen
Kies het kolomtype op basis van de kenmerken van je data:
Voor verschillende binaire gebruikssituaties selecteer je het juiste Microsoft SQL-datatype:
-- 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
Overweeg FILESTREAM voor grote bestanden
Voor grote bestanden (meer dan 1 MB) kunt u de Microsoft SQL FILESTREAM-functie overwegen, die gegevens opslaat in het bestandssysteem. FILESTREAM vereist server-side configuratie vóór gebruik:
# 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")
Valideer binaire gegevens
Valideer bestandstypen door magic bytes (bestandshandtekeningen) te controleren voordat binaire gegevens worden opgeslagen:
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})