如何使用astropy.io.fits.open()打开远程.FTS.gz文件?

时间:2017-04-05 01:56:06

标签: gzip astropy fits

问题摘要:

我正在编写一些代码,使用astropy.io.fits检查FTS文件头的内容(从望远镜保存的数据)。我的问题是当我尝试在远程服务器上打开.FTS.gz文件而不是.FTS文件时。当我open()一个.FTS.gz我收到错误时,如果我gunzip .FTS.gz文件,一切都很好。其中一个错误表明我有一张END缺失卡片。在线搜索,我使用ignore_missing_end=True中使用fits.open()参数的建议,但后来我得到了下一个错误。下一个错误表明我的FITS文件为空或已损坏,但情况并非如此。我可以使用SAOImage DS9打开它而没有任何问题,而且我已经运行this handy online tool名为fitsverify,它在我的文件中报告没有错误。如果我下载有问题的文件.FTS.gz并在本地运行类似代码fits.open()这个文件,我根本就没有错误。现在上传了一个违规文件示例(在下面的代码中使用){。{3}}。

Astropy文档说: "使用压缩文件 open()函数将无缝打开已使用gzip,bzip2或pkzip压缩的FITS文件。请注意,在此上下文中,我们讨论的是使用这些实用程序之一压缩的拟合文件 - 例如a .fits.gz文件。"

如何在不下载的情况下打开远程.FTS.gz文件?我有成千上万的这样的文件,所以下载不是一个选项,它不仅仅是一个问题的文件,它就是全部。

谢谢, 艾娜。

代码和错误:

打开远程.FTS.gz文件的代码:

from astropy.io import fits
import paramiko

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.load_system_host_keys()
client.connect('myhostname', username='myusername', password='mypassword')
apath = '/path/to/folder/to/search'
apattern = '"RUN0001.FTS.gz"'
rawcommand = 'find {path} -name {pattern}'
command = rawcommand.format(path=apath, pattern=apattern)
stdin, stdout, stderr = client.exec_command(command)
filelist = stdout.read().splitlines()
for i in filelist:
    sftp_client = client.open_sftp()
    remote_file = sftp_client.open(i)
    hdulist = fits.open(remote_file)
client.close()

ERROR:

Traceback (most recent call last):
  File "/Users/amusaeva/Documents/PyCharm/FITSHeaders/stackoverflow.py", line 17, in <module>
    hdulist = fits.open(remote_file)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 166, in fitsopen
    lazy_load_hdus, **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 404, in fromfile
    lazy_load_hdus=lazy_load_hdus, **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 1040, in _readfrom
    read_one = hdulist._read_next_hdu()
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 1135, in _read_next_hdu
    hdu = _BaseHDU.readfrom(fileobj, **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/base.py", line 329, in readfrom
    **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/base.py", line 394, in _readfrom_internal
    header = Header.fromfile(data, endcard=not ignore_missing_end)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/header.py", line 450, in fromfile
    padding)[1]
  File "/Library/Python/2.7/site-packages/astropy/io/fits/header.py", line 519, in _from_blocks
    raise IOError('Header missing END card.')
IOError: Header missing END card.

Process finished with exit code 1

仅更改一行以上的代码:

hdulist = fits.open(remote_file, ignore_missing_end=True)

ERROR:

WARNING: VerifyWarning: Error validating header for HDU #0 (note: Astropy uses zero-based indexing).
    Header size is not multiple of 2880: 7738429
There may be extra bytes after the last HDU or the file is corrupted. [astropy.io.fits.hdu.hdulist]
Traceback (most recent call last):
  File "/Users/amusaeva/Documents/PyCharm/FITSHeaders/stackoverflow.py", line 17, in <module>
    hdulist = fits.open(remote_file, ignore_missing_end=True)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 166, in fitsopen
    lazy_load_hdus, **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 404, in fromfile
    lazy_load_hdus=lazy_load_hdus, **kwargs)
  File "/Library/Python/2.7/site-packages/astropy/io/fits/hdu/hdulist.py", line 1044, in _readfrom
    raise IOError('Empty or corrupt FITS file')
IOError: Empty or corrupt FITS file

Process finished with exit code 1

打开违规行为的代码.FTS.gz文件本地不会产生错误:

import os
from astropy.io import fits

folderTosearch = "/path/to/folder/to/search/locally";
for root, dirs, files in os.walk(folderTosearch):
    for file in files:
        if file.endswith("RUN0001.FTS.gz"):
            hdulist = fits.open(os.path.join(root, file))

1 个答案:

答案 0 :(得分:2)

这是因为sftp调用传递了类文件对象的某种变体(它具有.read()将使用的fits.open()方法。
但是,像object这样的文件仍然是一个gzip文件。 Astropy检查文件是否仅压缩文件名称,即fits.open()的参数是字符串(恰好是路径)。 Astropy似乎没有测试将字节流标识为gzip文件的魔术字节。奇怪的是,它在传递路径字符串时会执行此验证。可以说,这可能是astropy.io.fits模块中的一个小缺点,但也许是有原因的。
(免责声明:上述结论是通过the relevant source code快速扫描;我可能错过了一些内容。如果有的话,希望有人会纠正我。)

一种解决方案是自己解压缩。我拼凑了以下内容:

from cStringIO import StringIO
import zlib

<...>

for i in filelist:                                                                       
    sftp_client = client.open_sftp()                                                     
    remote_file = sftp_client.open(i)                                                    
    decompressed = StringIO(                                                             
        zlib.decompress(remote_file.read(), zlib.MAX_WBITS|32))                          
    hdulist = fits.open(decompressed)                                                    
    client.close()                                                                       

上面,我们正在读取远程文件的全部内容(remote_file.read(),然后解压缩内容。这会产生一个字符串,因此我们将其包装在StringIO实例中以使其成为类似于文件的对象,我们可以传递给fits.open()。(对于zlib.MAX_WBITS|32参数:请参阅this answer。)

或者,您可以将文件sftp到本地磁盘,然后在本地读取文件(使用本地文件名)。以上只是将一切都记在内存中。