我有一个'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;
}
答案 0 :(得分:0)
WriteToStream方法。请先关闭文件。总是在那里使用finally块。
答案 1 :(得分:0)
没关系......调用J#代码的C#代码中的一个对象有一个终结器,它删除了J#代码所依赖的文件。 J#代码很好。病毒检查员不应该受到责备。我们刚刚在队伍中有一个破坏分子。垃圾收集器进来并收集仍然出现的对象在范围内。
答案 2 :(得分:0)
使用刷新功能更新文件信息