【问题标题】:Using SQL Server file streaming in Python在 Python 中使用 SQL Server 文件流式传输
【发布时间】:2019-12-18 01:47:02
【问题描述】:

我正在尝试在 python 中使用 SQL Server 2017 文件流。我使用的所有功能都通过 sqlalchemy,因此我试图找到一种使用它的方法,因为我没有在 sqlalchemy 或其他库中找到任何实现(可能遗漏了一些东西,如果是这样,请指出我的工作和测试实现)。

我决定使用 dll 来解决这个问题,基于 https://github.com/VisionMark/django-mssql-filestream/blob/master/sql_filestream/win32_streaming_api.py 。但是,我对 OpenSqlFilestream 的调用失败并返回 -1 而不是文件句柄。我不知道问题是什么或如何解决它。

from ctypes import c_char, sizeof, windll
from sqlalchemy import create_engine
from sqlalchemy.orm import session_maker
import msvcrt
import os

msodbcsql = windll.LoadLibrary("C:\Windows\System32\msodbcsql17.dll") 

engine = create_engine("mssql+pyodbc://user:pass@test/test?TrustedConnection=yes+driver=ODBC Driver+17+for+SQL+Server")
maker = session_maker(bind=engine)
session = session_maker()
## first query should begind transaction
path = session.execute("SELECT file_stream.PathName() FROM test_filetable").fetchall()[0][0]
## this returns str like "\\\\test\\*"
context = session.execute("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()").fetchall()[0][0]
## returns bytes

_context = (c_char*len(context)).from_buffer_copy(context)

## This call fails
handle = msodbcsql.OpenSqlFilestream(
        path, # FilestreamPath
        0, # DesiredAccess
        0, # OpenOptions
        _context, # FilestreamTransactionContext
        sizeof(_context), # FilestreamTransactionContextLength
        0 # AllocationSize
    )
## this returns -1 instead of handle

## Never reached, but this should create usable file
desc = msvcrt.open_osfhandle(fsHandle, os.O_RDONLY)
_file = os.fdopen(desc, 'r')

所有查询都有效并输出(据我所知)正确的数据。

如何从 python (3.7) 获取对 SQL Server 2017 上文件的文件流访问?

编辑:我读取的对象达到千兆字节的大小,进程只需要流访问。

【问题讨论】:

  • 在失败的语句中,sqlncli是什么?你真的是说msodbcsql吗?此外,FILESTREAM 列中的对象是否真的足够大,以至于您必须跳过箍才能获得它们?如果它们不是太大,那么您可以将FILESTREAM 列视为常规varbinary(max) 列,即SELECT file_stream FROM ...)。
  • 抱歉错字,我正在重新输入我发布的 git 中的代码,因为我当时无法访问我的仓库。我只是在示例中错误地命名了变量。这些文件远大于我的进程需要的大小或它可以使用的内存量(文件大小高达千兆字节)。因此我需要文件流。

标签: python sqlalchemy pyodbc sql-server-2017 msodbcsql17


【解决方案1】:

我的猜测是你的问题与

有关
  1. SQLAlchemy 会话不仅仅是一个原始 DB API 连接这一事实,和/或
  2. 事务上下文不适合您调用OpenSqlFilestream

对于它的价值,以下适用于 CPython 3.7.2 和 pythonnet 2.4.0:

import clr
clr.AddReference("System.Data")
from System.Data import IsolationLevel
from System.Data.SqlClient import SqlCommand, SqlConnection
from System.Data.SqlTypes import SqlFileStream
from System.IO import File, FileAccess, FileOptions

# adapted from c# code at
# https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/filestream-data
connection_string = r"Data Source=(local)\SQLEXPRESS;Initial Catalog=myDB;Integrated Security=True"
con = SqlConnection(connection_string)
con.Open()
sql = """\
SELECT Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT()
FROM employees WHERE EmployeeID = 1"""
cmd = SqlCommand(sql, con)
tran = con.BeginTransaction(IsolationLevel.ReadCommitted)
cmd.Transaction = tran
rdr = cmd.ExecuteReader()
rdr.Read()
path = rdr.GetString(0)
transaction_context = rdr.GetSqlBytes(1).Buffer
rdr.Close()
allocation_size = 0
input_stream = SqlFileStream(path, transaction_context,
        FileAccess.Read, FileOptions.SequentialScan, allocation_size)
output_stream = File.Create(r"C:\Users\Gord\Desktop\photo.bmp")
input_stream.CopyTo(output_stream)
output_stream.Close()
input_stream.Close()
tran.Commit()
con.Close()

【讨论】:

    猜你喜欢
    • 2016-08-14
    • 2020-09-15
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 2019-05-16
    • 2019-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多