重构在 SQL 数据库中存储为 RAW 的 PNG 文件

Posted

技术标签:

【中文标题】重构在 SQL 数据库中存储为 RAW 的 PNG 文件【英文标题】:Reconstitute PNG file stored as RAW in SQL Database 【发布时间】:2015-07-22 17:24:46 【问题描述】:

我正在努力从 SQL 数据库 (Windows SQL Server) 编写报告,这将要求某些人在将报告提交给客户之前签署报告。我们希望有一个系统,这些人可以在数据库中授权他们的签名,然后我们可以使用存储在数据库中的他们签名的图像并将其放在 LaTeX 生成的报告上。

签名图像被创建为 PNG,然后存储在数据库中类型为 varbinary 的字段中。为了在报告中使用签名,我需要将 PNG 重构为我可以在 LaTeX 中使用 \includegraphics 的文件。

不幸的是,我似乎无法从数据库中重新创建 PNG。由于我无法发布签名,我们将使用下面的图片作为示例。

在我的计算机上使用此图像,我可以读取原始文件,将其写入不同的文件,并在打开新文件时获得相同的图像。

#* It works to read the image from a file and rewrite it elsewhere
pal <- readBin("C:/[filepath]/ColorPalette.png",
          what = "raw", n = 1e8)
writeBin(pal,
         "C:/[filepath]/colors.png",
         useBytes=TRUE)

现在,我已将相同的图像保存到数据库中,并使用 RODBC,我可以像这样提取它:

#*** Capture the raw from the database
con <- odbcConnect("DATABASE")
Users <- sqlQuery(con, "SELECT * FROM dbo.[User]")

db_pal <- Users$Signature[Users$LastName == "MyName"]

#*** Write db_pal to a file, but the image won't render
#*** Window Photo Viewer can't open this picture because the file appears to be damaged, corrupted, or is too large (12KB)

writeBin(db_pal[[1]],
         "C:/[filename]/db_colors.png",
         useBytes=TRUE)

对象paldb_pal 在this Gist 中定义(它们太长,无法放入此处允许的空间)

注意:db_pal 是一个原始向量的列表。另外,它明显不同于原始向量pal

> length(pal)
[1] 2471
> length(db_pal[[1]])
[1] 9951

有什么想法可以让我从数据库中取出这张图片吗?

【问题讨论】:

你知道图片的尺寸(逐列)吗? 587 像素宽 x 496 像素高。 【参考方案1】:

嗯,我们已经找到了解决方案。通过 RODBC 返回的原始向量与 SQL 数据库中的内容不匹配。在管道的某个地方,来自 SQL 的 varbinary 对象被扭曲了。我不确定为什么或如何。但是this answer to a different problem 启发了我们重铸变量。一旦我们重铸它们,我们就可以看到正确的表示。

下一个问题是我们所有的图像都超过 8000 字节,而 RODBC 一次只允许 8000 个字符。所以我不得不摸索着解决这个问题。下面的代码执行以下操作:

    确定图像文件中的最大字节数 创建一组变量 (ImagePart1, ..., ImagePart[n]) 将图像分成尽可能多的部分,每个部分的最大长度为 8000。 在数据库中查询所有图像。 将图像部分组合成一个对象 将图像写入本地文件。

实际代码

library(RODBC)

lims <- odbcConnect("DATABASE")

#* 1. Determine the largest number of bytes in the largest image file
ImageLength <- sqlQuery(lims, 
                            paste0("SELECT MaxLength = MAX(LEN(u.Image)) ",
                                   "FROM dbo.[User] u"))

#* Create a query string to make a set of variables breaking
#* the images into as many parts as necessary, each with 
#* max length 8000
n_img_vars <- ImageLength$MaxLength %/% 8000 + 1

start <- 1 + 8000 * (0:(n_img_vars - 1))
end <- 8000 + 8000 * (0:(n_img_vars - 1))

img_parts <- paste0("ImagePart", 1:n_img_vars, 
                    " = CAST(SUBSTRING(u.Image, ", start,
                    ", ", end, ") AS VARBINARY(8000))")

full_query <- paste0("SELECT u.OID, u.LastName, u.FirstName,\n",
                     paste0(img_parts, collapse =",\n"), "\n",
                     "FROM dbo.[User] u \n",
                     "WHERE LEN(u.Image) > 0")

#* 3. Query the database for all the images
Images <- sqlQuery(lims, full_query)

#* 4. Combine the images parts into a single object
Images$full_image <- 
  apply(Images[, grepl("ImagePart", names(Images))], 1, 
        function(x) do.call("c", x))

#* 5. Write the images to a local file
for(i in seq_len(nrow(Images)))
  DIR <- "[FILE_DIR]"
  FILENAME <- with(Images, paste0(OID[i], "-", LastName[i], ".png"))
  writeBin(unlist(Images$full_image[i]),
           file.path(DIR, FILENAME))

【讨论】:

【参考方案2】:

我可能误解了这个问题,但raster 包可能会对您有所帮助。

library(raster)
your_image <- raster(nrows=587,ncols=496,values=db_pal[[1]])
plot(your_image)

但是db_pal[[1]] 的长度不是 291,152 (587*496) 是没有意义的,所以对我来说没有什么意义。您知道这 291,152 个值将存储在哪里吗?

【讨论】:

以上是关于重构在 SQL 数据库中存储为 RAW 的 PNG 文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 Imagemagick 将 RAW 转换为 PNG

使用 C 将 .raw 文件数据存储为指针

RAW和JPG这两个格式有啥区别

图像转换 - 用于游戏的 RAW 到 png/raw (Pac The Man X)

SQL Server存储过程

为啥我重构的 SQL 比原来的版本效率低?