什么可能导致FileStream从明显锁定的文件中读取0个字节?

时间:2011-08-16 05:33:56

标签: .net file-io j#

我有一个'pack files'方法,用J#编写,它接受一堆文件并将它们全部写入流中,还有一些足够的元数据,可以通过相关的“解包”方法将它们重建为单个文件。我在这个问题的最后提供了一个粗略的版本,可以很好地说明底层J#的作用。我通过在J#代码上运行.NET反射器来生成等效的J#库。

现在,下面的代码在开发中完美运行,但间歇性地在生产中遇到错误。以下评论中标注为“错误1”和“错误2”的例外情况都是在野外看到的。在出现“ERROR 2”的情况下,总是写入0个字节。发生这些错误时没有明显的模式。涉及的文件大小通常低于100kb。

最近创建了传递给'pack'方法的文件,就像毫秒之前一样,创建了挂钟时间。输出流指向新创建的文件,没有共享。

总而言之,对于我知道存在的文件,我有时会得到一个'0'的文件长度...因为我刚刚创建了它。由于J#的介入,我无法获得实际的异常。 (当然,如果我正在调试,我可以打破第一次机会异常,但正如所提到的,这在开发环境中永远不会发生)。其他时候我无法读取文件中的任何字节,即使它已成功打开。在复制过程中有异常,我只能假设'Read'返回-1。

这可能会发生什么?有任何想法吗?我怀疑是在Prod中运行了一个病毒检查,但是没有dev,并且可能它以某种方式涉及。但是当我打开并锁定文件(如在WriteToStream方法中)​​时,病毒检查程序会做什么,这会导致它在没有错误的情况下停止读取?我已经编写了一个可以锁定任意文件的小测试应用程序......但是从另一个进程锁定文件似乎并没有阻止FileInfo.Length工作,一旦文件流打开,我的测试应用程序就无法锁定文件,如你所料。

我很难过,我是。

编辑:

好的 - 这是J#代码。重新标记了这个问题。

编辑2:

我还应该提一下,为了解决问题的目的,后来添加了0长度的检查。在此之前,在将“长度”与“书面”进行比较之后总是失败。 因此,每当'length'不等于'written'时,有时'length'为0,有时'write'为0.我相信问题不是我的代码中的错误,而是由外部因素引起的。这个问题的目的是找出另一个进程(例如病毒检查程序)可以对这些文件做什么导致我的代码以我描述的方式失败。

public static void packContentsToStream(Serializable serializable, Stream stream)
{
    try
    {
        OutputStream output = new StreamWrapperOutputStream(stream);

        FileRecordPair[] recordPairs = SerializationUtil.getRecords(serializable);
        FileRecord[] records = new FileRecord[recordPairs.length];
        File[] files = new Files[recordPairs.length];

        for (int i = 0; i < recordPairs.length; i++)
        {
            FileRecordPair pair = recordPairs[i];
            records[i] = pair.getRecord();
            files[i] = pair.getFile();
        }

        SerializationUtil.writeToStream(serializable, output, false); // False keeps stream open
        SerializationUtil.writeToStream(records, output, false);

        for (int i = 0; i < files.length; i++)
        {
            File file = files[i];
            long written = writeToStream(file, output);
            if (written != records[i].getFileLength())
            {
                throw new SystemException("Invalid record. The number of bytes written [" + written + "] did not match the recorded file length [" + records[i].getFileLength() + "]."); // ERROR 2
            }
        }
     }
     catch (Exception e)
     {
        throw new SystemException("Could not write FileRecords", e);
     }
}


public static long writeToStream(HapiFile file, OutputStream stream)
{
    long written = 0;
    if (file.exists())
    {
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(file);
            written = copy(fis, stream);
        }
        catch (Exception e)
        {
            throw new SystemException("Could not write file to stream", e);
        }
        finally
        {
            if (fis != null)
            {
                try
                {
                    fis.close();
                }
                catch (IOException ioe)
                {
                    // For now - throw an exception to see if this might be causing the packing error
                    throw new SystemException("Error closing file", ioe);
                }
            }
        }
    }

    return written;
}

public static int copy(InputStream is, OutputStream stream) throws IOException
{
    int total = 0;
    int read = 0;
    byte[] buffer = new byte[BUFFER_SIZE];

    while (read > -1)
    {
        read = is.read(buffer);
        if (read > 0)
        {
            stream.write(buffer, 0, read);
            total += read;
        }
    }

    return total;
}

// Relevant part of 'SerializationUtil.getRecords'

private static FileRecord GetFor(File file, String recordName, int index, String pathToInstance) 
{
    String fileName = file.getName();
    int length = (int) file.length(); // Safe as long as file is under 2GB

    if (length == 0) 
    {
        throw new SystemException("Could not obtain file length for '" + file.getPath() + "'"); // ERROR 1
    }

    if (index > -1 && recordName != null && recordName.length() > 0) 
    {
        recordName = recordName + "." + index;
    }

    return new FileRecord(fileName, length, recordName, pathToInstance);
}

// File.length() implementation - obtained using .NET Reflector
public virtual long length()
{
    long length = 0L;
    if (this.__fileInfo != null)
    {
        SecurityManager manager = System.getSecurityManager();
        if (manager != null)
        {
            manager.checkRead(this.__mCanonicalPath);
        }
        try
        {
            this.__fileInfo.Refresh();
            if (!this.exists() || !(this.__fileInfo is FileInfo))
            {
                return 0L;
            }
            length = ((FileInfo) this.__fileInfo).Length;
        }
        catch (FileNotFoundException)
        {
        }
        catch (IOException)
        {
        }
    }
    return length;
}

3 个答案:

答案 0 :(得分:0)

WriteToStream方法。请先关闭文件。总是在那里使用finally块。

答案 1 :(得分:0)

没关系......调用J#代码的C#代码中的一个对象有一个终结器,它删除了J#代码所依赖的文件。 J#代码很好。病毒检查员不应该受到责备。我们刚刚在队伍中有一个破坏分子。垃圾收集器进来并收集仍然出现的对象在范围内。

答案 2 :(得分:0)

使用刷新功能更新文件信息