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

时间:2015-07-22 17:24:46

标签: r rodbc

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

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

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

enter image description here

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

#* 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_palthis Gist中定义(它们太长,无法放入允许的空间)

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

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

有关将图像从数据库中删除可能需要做些什么的想法?

2 个答案:

答案 0 :(得分:2)

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

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

  1. 确定图像文件中的最大字节数
  2. 创建一组变量(ImagePart1,...,ImagePart[n]),根据需要将图像分成多个部分,每个部分的最大长度为8000.
  3. 在数据库中查询所有图像。
  4. 将图像部分合并为单个对象
  5. 将图像写入本地文件。
  6. 实际代码

    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))
    }
    

答案 1 :(得分:0)

我可能会误解这个问题,但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值会被存储在哪里吗?