簡體   English   中英

什么可能導致 FileStream 從明顯鎖定的文件中讀取 0 個字節?

[英]What could cause FileStream to read 0 bytes from an apparently locked file?

我有一個“打包文件”方法,用 J# 編寫,它需要一堆文件並將它們全部寫入 stream,以及一些足夠的元數據,可以通過相關的“解包”方法將它們重建為單個文件。 我在這個問題的末尾提供了一個粗略的版本,它很好地說明了底層 J# 的作用。 我通過在 J# 代碼上運行 .NET 反射器來生成等效的 J# 庫來獲得這個。

現在下面的代碼在開發中完美運行,但在生產中間歇性地遇到錯誤。 以下評論中標記為“錯誤 1”和“錯誤 2”的異常都已在野外看到。 在發生“錯誤 2”的情況下,總是寫入 0 個字節。 這些錯誤發生的時間沒有明顯的規律。 所涉及的文件大小通常低於 100kb。

傳入“pack”方法的文件是最近創建的,就像毫秒前的掛鍾時間一樣。 output stream 指向一個新創建的沒有共享的文件。

總而言之,有時我會為我知道存在的文件返回“0”的文件長度......因為我剛剛創建了它。 由於 J# 的參與,我無法獲得實際的異常。 (當然,如果我正在調試,我可能會在第一次機會異常時中斷,但如前所述,這在開發環境中永遠不會發生)。 其他時候,即使文件已成功打開,我也無法從文件中讀取任何字節。 復制過程中出現異常,我只能假設'Read'返回-1。

這里會發生什么? 有任何想法嗎? 我的懷疑是在 Prod 中檢查了一個病毒,但沒有在 dev 中運行,並且它可能以某種方式參與其中。 但是,當我打開並鎖定一個文件(如在 WriteToStream 方法中)時,病毒檢查器會做什么,這會導致它停止讀取而不會出現錯誤? 我已經編寫了一個可以鎖定任意文件的小測試應用程序......但是從另一個進程鎖定文件似乎並不能阻止 FileInfo.Length 工作,一旦文件 stream 打開,我的測試應用程序就不能再鎖定文件,如您所料。

我被難住了,我是。

編輯:

好的 - 這里是 J# 代碼。 已重新標記問題。

編輯2:

我還應該提到,為了排除故障,后來添加了對 0 長度的檢查。 在此之前,將“長度”與“書面”進行比較后總是失敗。 因此,每當“長度”不等於“寫入”時,有時“長度”為 0,有時“寫入”為 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;
}

使用刷新 function 更新文件信息

WriteToStream 方法。 請先關閉文件。 總是在那里使用 finally 塊。

沒關系... C# 代碼中調用 J# 代碼的對象之一有一個終結器,它刪除了 J# 代碼所依賴的文件。 J# 代碼很好。 病毒檢查器不應該受到責備。 我們只是有一個破壞者在隊伍中。 垃圾收集器進來並收集 object,它似乎仍在 scope 中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM