Error while downloading Lists in a spweb - c#

Here is the code
private void downloadList(SPObjectData objectData)
{
using (SPWeb currentWeb = objectData.Web)
{
foreach (SPList list in currentWeb.Lists)
{
foreach (SPFolder oFolder in list.Folders)
{
if (oFolder != null)
{
foreach (SPFile file in oFolder.files)
{
if (CreateDirectoryStructure(tbDirectory.Text, file.Url))
{
var filepath = System.IO.Path.Combine(tbDirectory.Text, file.Url);
byte[] binFile = file.OpenBinary();
System.IO.FileStream fstream = System.IO.File.Create(filepath);
fstream.Write(binFile, 0, binFile.Length);
fstream.Close();
}
}
}
}
}
}
}
Error while compilation
Error Unable to cast object of type 'Microsoft.SharePoint.SPListItem' to type 'Microsoft.SharePoint.SPFolder'.
Error coming on line " foreach (SPFolder oFolder in list.Folders)
I am trying to assign a folder in list. folders to folder but for some reason it giving error mentioned above.
I was trying to get folders from a Lists but after reading difference between folder and list objects on link given below, i changed my code as mentioned below, cheers
enter link description here
private void downloadList(SPObjectData objectData)
{
using (SPWeb currentWeb = objectData.Web)
{
foreach (SPFolder oFolder in currentWeb.Folders)
{
if (oFolder != null)
{
foreach (SPFile file in oFolder.Files)
{
if (CreateDirectoryStructure(tbDirectory.Text, file.Url))
{
var filepath = System.IO.Path.Combine(tbDirectory.Text, file.Url);
byte[] binFile = file.OpenBinary();
System.IO.FileStream fstream = System.IO.File.Create(filepath);
fstream.Write(binFile, 0, binFile.Length);
fstream.Close();
}
}
}
}
}
}

try
foreach (SPFile file in oFolder.Files)
Update: If there is a problem just index into it:
SPFileCollection collFiles = oFolder.Files;
long lngTotalFileSize = 0;
for (int intIndex = 0; intIndex < collFiles.Count; intIndex++)
{
lngTotalFileSize += collFiles[intIndex].Length;
}
HTH

Related

XML Serialization leaves file blank after restart

We have a problem where our industrial equipments software's .XML settings files become blank, yet they still have the correct number of bytes.
I have a feeling it might be caused by the way the customers are shutting down the PC as it tends to happen after they've down a shutdown, isolate, and boot. The way I save the files is,
Serialize to %temp% file
Validate that the newly created file starts with <?xml
If the /backup folders version of the file is older than a day, copy the existing file to the /backup folder
Copy new file to overwrite existing file.
I thought maybe it's related to encoding, disk caching, Windows Update, or Windows Recovery.
Looking for ideas as I've spent two years chasing down why this is happening.
As per request, here is the code.
public static bool SerializeObjXml(object Object2Serialize, string FilePath, Type type, bool gzip = false)
{
if (!Path.IsPathRooted(FilePath))
FilePath = Path.Combine(ApplicationDir, FilePath);
bool isSuccess = false;
var tmpFile = Path.GetTempFileName();
try
{
for (int i = 0; i < 3; i++)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
if (gzip)
{
using (var ms = new MemoryStream())
{
XmlSerializer bf = new XmlSerializer(type);
bf.Serialize(ms, Object2Serialize);
ms.Position = 0;
using (var fileStream = new BinaryWriter(File.Open(tmpFile, FileMode.Create)))
{
using (GZipStream gzipStream = new GZipStream(fileStream.BaseStream, CompressionMode.Compress))
{
byte[] buffer = new byte[4096];
int numRead;
while ((numRead = ms.Read(buffer, 0, buffer.Length)) != 0)
{
gzipStream.Write(buffer, 0, numRead);
}
}
}
}
if (!FileChecker.isGZip(tmpFile))
throw new XmlException("Failed to write valid XML file " + FilePath);
}
else
{
using (var fs = new StreamWriter(File.Open(tmpFile, FileMode.Create), Encoding.UTF8))
{
XmlSerializer bf = new XmlSerializer(type);
bf.Serialize(fs, Object2Serialize);
}
if (!FileChecker.isXML(tmpFile))
throw new XmlException("Failed to write valid XML file " + FilePath);
}
isSuccess = true;
return true;
}
catch (XmlException)
{
return false;
}
catch (System.IO.DriveNotFoundException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
catch (System.IO.FileNotFoundException) { continue; }
catch (System.IO.IOException) { continue; }
}
}
finally
{
if (isSuccess)
{
lock (FilePath)
{
try
{
//Delete existing .bak file
if (File.Exists(FilePath + ".bak"))
{
File.SetAttributes(FilePath + ".bak", FileAttributes.Normal);
File.Delete(FilePath + ".bak");
}
}
catch { }
try
{
//Make copy of file as .bak
if (File.Exists(FilePath))
{
File.SetAttributes(FilePath, FileAttributes.Normal);
File.Copy(FilePath, FilePath + ".bak", true);
}
}
catch { }
try
{
//Copy the temp file to the target
File.Copy(tmpFile, FilePath, true);
//Delete .bak file if no error
if (File.Exists(FilePath + ".bak"))
File.Delete(FilePath + ".bak");
}
catch { }
}
}
try
{
//Delete the %temp% file
if (File.Exists(tmpFile))
File.Delete(tmpFile);
}
catch { }
}
return false;
}
public static class FileChecker
{
const string gzipSig = "1F-8B-08";
static string xmlSig = "EF-BB-BF";// <?x";
public static bool isGZip(string filepath)
{
return FileChecker.CheckSignature(filepath, (3, gzipSig)) != null;
}
public static bool isXML(string filepath)
{
return FileChecker.CheckSignature(filepath, (3, xmlSig)) != null;
}
public static bool isGZipOrXML(string filepath, out bool isGZip, out bool isXML)
{
var sig = FileChecker.CheckSignature(filepath, (3, gzipSig), (3, xmlSig));
isXML = (sig == xmlSig);
isGZip = (sig == gzipSig);
return isXML || isGZip;
}
public static string CheckSignature(string filepath, params (int signatureSize, string expectedSignature)[] pairs)
{
if (String.IsNullOrEmpty(filepath))
throw new ArgumentException("Must specify a filepath");
if (String.IsNullOrEmpty(pairs[0].expectedSignature))
throw new ArgumentException("Must specify a value for the expected file signature");
int signatureSize = 0;
foreach (var pair in pairs)
if (pair.signatureSize > signatureSize)
signatureSize = pair.signatureSize;
using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
if (fs.Length < signatureSize)
return null;
byte[] signature = new byte[signatureSize];
int bytesRequired = signatureSize;
int index = 0;
while (bytesRequired > 0)
{
int bytesRead = fs.Read(signature, index, bytesRequired);
bytesRequired -= bytesRead;
index += bytesRead;
}
foreach (var pair in pairs)
{
string actualSignature = BitConverter.ToString(signature, 0, pair.signatureSize);
if (actualSignature == pair.expectedSignature)
return actualSignature;
}
}
return null;
}
}
Using the operating system's move or copy file to overwrite an existing file is an atomic operation meaning the it wholly succeeds or doesn't and doesn't overlap other file operations.
Therefore what you have should work if that is how you are achieving step 4.
Copy new file to overwrite existing file.
If instead you are blanking out the existing file and re-writing the data I suspect that could be the the point of failure..
The issues while file space is being allocated the write is not occurring during shutdown, which leaves you when a file with bytes allocated without the data being flushed to disk.
During the OS shutdown, likely a ThreadAbortException is raised which triggers your finally block.
You can attempt to reproduce by calling Process.Start("shutdown", "-a") before your return statement but after you have set success = true.
I would suggest simplifying your code and have everything run inside of your try {} statement. This removes the possibility of having a state where success = true before your attempted your write to disk, which is then triggered in a finally statement trigged by a windows shutdown.
public static bool SerializeObjXml(
object Object2Serialize,
string FilePath,
Type type,
bool gzip = false)
{
if (!Path.IsPathRooted(FilePath))
FilePath = Path.Combine(ApplicationDir, FilePath);
Directory.CreateDirectory(FilePath);
for (int i = 0; i < 3; i++)
{
try
{
var tempFi = SerializeToXmlFile(Object2Serialize, type, gzip);
var fi = new FileInfo(FilePath);
if (fi.Exists)
fi.CopyTo(fi.FullName + ".bak", true);
tempFi.CopyTo(fi.FullName, true);
tempFi.Delete();
return true;
}
catch (Exception ex)
{
string message = $"[{DateTime.Now}] Error serializing file {FilePath}. {ex}";
File.WriteAllText(FilePath + ".log", message);
}
}
return false;
}
As a side note, you can simply use [Stream.CopyTo][1] and write directly to your temp file, without the need for intermediary streams or for manual buffer/byte read/write operations:
private static FileInfo SerializeToXmlFile(
object Object2Serialize,
Type type,
bool gzip)
{
var tmpFile = Path.GetTempFileName();
var tempFi = new FileInfo(tmpFile);
if (!gzip)
{
using (var fs = File.Open(tmpFile, FileMode.Create))
(new XmlSerializer(type)).Serialize(fs, Object2Serialize);
if (!FileChecker.isXML(tmpFile))
throw new Exception($"Failed to write valid XML file: {tmpFile}");
}
else
{
using (var fs = File.Open(tmpFile, FileMode.CreateNew))
using (var gz = new GZipStream(fs, CompressionMode.Compress))
(new XmlSerializer(type)).Serialize(fs, Object2Serialize);
if (!FileChecker.isGZip(tmpFile))
throw new Exception($"Failed to write valid XML gz file: {tmpFile}");
}
return tempFi;
}

Out of memory exception even for a very small task

I have a TCP application where I can request images from a folder on the server from the client. If I request a small folder, it works fine. If its a big folder it will throw an out of memory exception. But then anything after that, even a folder with 1 file will throw the same out of memory exception.
I thought it might have been the thread that is out of memory, so I tried to put it on a separate thread and task but neither worked. Here is the code I'm using:
public static void Images(string path)
{
new Task(() =>
{
try
{
string root = lookupDirectoryPath("Application data");
string backupPath = root + #"\Apple Computer\MobileSync\";
string imagePath = backupPath + path;
if (Directory.Exists(imagePath))
{
String[] allfiles = Directory.GetFiles(imagePath, "*.*", SearchOption.AllDirectories);
List<Image> allImages = new List<Image>();
foreach (string file in allfiles)
{
using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
if (IsImage(stream))
{
allImages.Add(Image.FromFile(file));
}
}
}
if (allImages.Count > 0)
{
byte[] data = imageListToByteArray(allImages);
serverSendByteArray(data, 12);
}
else
{
serverSendByteArray(Encoding.Default.GetBytes("backup contained no images"), 1);
}
}
else
{
serverSendByteArray(Encoding.Default.GetBytes("iphone backup folder does not exist"), 1);
}
}
catch (Exception ex)
{
if (ex.GetType().IsAssignableFrom(typeof(OutOfMemoryException)))
{
serverSendByteArray(Encoding.Default.GetBytes("Out of memory, could not send iphone images"), 1);
}
else
{
serverSendByteArray(Encoding.Default.GetBytes("Unknown error, could not send iphone images"), 1);
}
}
}).Start();
}
The exception gets thrown at allImages.Add(Image.FromFile(file));
this is the isImage() function:
public static bool IsImage(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
List<string> jpg = new List<string> { "FF", "D8" };
List<string> bmp = new List<string> { "42", "4D" };
List<string> gif = new List<string> { "47", "49", "46" };
List<string> png = new List<string> { "89", "50", "4E", "47", "0D", "0A", "1A", "0A" };
List<List<string>> imgTypes = new List<List<string>> { jpg, bmp, gif, png };
List<string> bytesIterated = new List<string>();
for (int i = 0; i < 8; i++)
{
string bit = stream.ReadByte().ToString("X2");
bytesIterated.Add(bit);
bool isImage = imgTypes.Any(img => !img.Except(bytesIterated).Any());
if (isImage)
{
return true;
}
}
return false;
}
Thanks for any help
I tried and I can reproduce your problem. It is definitely out of memory and nothing like "It just seems to be" the memory usage increases to about 4 GB and then the error shows up. Console output is just to see what's happening there.
The Image object seems to be not the best way to save the data.
I tried this and got this to work with many many files. Maybe you can change the code to fit your needs:
String[] allfiles = Directory.GetFiles(imagePath, "*.*", SearchOption.AllDirectories);
//List<Image> allImages = new List<Image>();
List<Byte[]> allImagesBytes = new List<Byte[]>();
foreach (string file in allfiles)
{
using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
if (IsImage(stream))
{
Console.Clear();
Console.Write(allImagesBytes.Count());
//allImages.Add(Image.FromStream(stream));
//allImages.Add(Image.FromFile(file));
allImagesBytes.Add(File.ReadAllBytes(file));
}
}
}
Image.FromFile seem to cause the error. In the following question it was a corrupeted image file or running out of file handles, Image.FromStream() did it better. Worth a try cause you already have the stream open:
https://stackoverflow.com/a/2216338/7803013
Try changing this
using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
if (IsImage(stream))
{
allImages.Add(Image.FromFile(file));
}
}
...
if (allImages.Count > 0)
{
byte[] data = imageListToByteArray(allImages);
serverSendByteArray(data, 12);
}
into this:
using (FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
if (IsImage(stream))
{
allImages.Add(Image.FromFile(file));
}
stream.Close();
}
....
if (allImages.Count > 0)
{
byte[] data = imageListToByteArray(allImages);
foreach(Image img in allImages)
{
img.Dispose();
}
serverSendByteArray(data, 12);
}

Using Renci ssh to upload to SFTP directory

I am not getting an error but my files are not getting uploaded. Am trying to upload to a targetDirectory on SFTP.
public string TryUploads(string targetDirectory)
{
string _localDirectory = LocalDirectory; //The directory in SFTP server where the files are present
if (oSftp == null)
{
oSftp = Instance;
}
lock (thisLock)
{
try
{
oSftp.Connect();
List<string> fileList = Directory.GetFiles(_localDirectory, "*.*").ToList<string>();
oSftp.ChangeDirectory(targetDirectory);
if (fileList != null && fileList.Count() > 1)
{
for (int i = 0; i < fileList.Count(); i++)
{
string ftpFileName = Path.GetFileName(fileList[i]);
if (!String.IsNullOrEmpty(targetDirectory))
ftpFileName = String.Format("{0}/{1}", targetDirectory, ftpFileName);
using (var stream = new FileStream(Path.GetFileName(fileList[i]), FileMode.Create))
{
oSftp.BufferSize = 4 * 1024;
oSftp.UploadFile(stream, ftpFileName);
// stream.Close();
}
}
}
oSftp.Disconnect();
}
catch (Exception e)
{
throw new ApplicationException(e.Message);
}
}
return Strings.StatusOk;
}
I solved the issue. I needed to put the location of where am getting the file from in the stream
using (var stream = new FileStream(LocalDirectory + "\\" + Path.GetFileName(fileList[i]), FileMode.Open))
Solved. Files uploaded ;)

SearchOption.AllDirectories filter

I am trying to filter out the path C:\$Recycle.bin in my file enumeration. How can I do this?
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).OrderBy(p => p).ToList();
When I execute the above, I get the error below.
Additional information: Access to the path 'C:\$Recycle.Bin\S-1-5-21-1600837348-2291285090-976156579-500' is denied.
I also want to calc every file's md5. I have:
var mainDirectory = new DirectoryInfo("\\");
var files = GetDirectories(mainDirectory);
List<string> drives = new List<string>();
foreach (var file in files)
{
//Console.WriteLine(file.Name);
drives.Add(mainDirectory + file.Name);
}
MD5 md5 = MD5.Create();
foreach (string file in drives)
{
// hash path
string relativePath = file.Substring("\\".Length + 1);
byte[] pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
// hash contents
try
{
byte[] contentBytes = File.ReadAllBytes(file);
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
}
catch(UnauthorizedAccessException)
{
continue;
}
catch
{
continue;
}
Console.WriteLine(BitConverter.ToString(md5.Hash).Replace("-", "").ToLower());
}
Console.ReadKey();
The following could do it for you, but it is a quick and dirty way, because it does not handle any exceptions. I did not regard any readability and it is not fully tested.
static void Main(string[] args)
{
var mainDirectory = new DirectoryInfo("C:\\");
var files = GetFiles(mainDirectory, ".");
foreach (var file in files)
{
Console.WriteLine(file.Name);
}
Console.ReadKey();
}
static IEnumerable<DirectoryInfo> GetDirectories(DirectoryInfo parentDirectory)
{
DirectoryInfo[] childDirectories = null;
try
{
childDirectories = parentDirectory.GetDirectories();
}
catch (Exception)
{
}
yield return parentDirectory;
if (childDirectories != null)
{
foreach (var childDirectory in childDirectories)
{
var childDirectories2 = GetDirectories(childDirectory);
foreach (var childDirectory2 in childDirectories2)
{
yield return childDirectory2;
}
}
}
}
static IEnumerable<FileInfo> GetFiles(DirectoryInfo parentDirectory,
string searchPattern)
{
var directories = GetDirectories(parentDirectory);
foreach (var directory in directories)
{
FileInfo[] files = null;
try
{
files = directory.GetFiles(searchPattern);
}
catch (Exception)
{
}
if (files != null)
{
foreach (var file in files)
{
yield return file;
}
}
}
}

can I download entire SPSite or SPWeb to harddisk (ATM i can only download 1 folder)

I made a windows form application which maps whole of spsite (given in text box) in form of tree View, but i am wondering if user selects to download whole Site, what code i will be required, i looked into google but find code to download one file or folder which is given below,
Downloading a folder
private void bFolder_Click(object sender, EventArgs e)
{
TreeNode currentNode = TreeFolder.SelectedNode;
SPFolder oFolder = (SPFolder)currentNode.Tag;
foreach (SPFile file in oFolder.Files)
{
if (CreateDirectoryStructure(tbDirectory.Text, file.Url))
{
var filepath = System.IO.Path.Combine(tbDirectory.Text, file.Url);
byte[] binFile = file.OpenBinary();
System.IO.FileStream fstream = System.IO.File.Create(filepath);
fstream.Write(binFile, 0, binFile.Length);
fstream.Close();
}
}
}
//creating directory
private bool CreateDirectoryStructure(string baseFolder, string filepath)
{
if (!Directory.Exists(baseFolder)) return false;
var paths = filepath.Split('/');
for (var i = 0; i < paths.Length - 1; i++)
{
baseFolder = System.IO.Path.Combine(baseFolder, paths[i]);
Directory.CreateDirectory(baseFolder);
}
return true;
}
Try this:
SPSite oSiteCollection = SPContext.Current.Site;
SPWebCollection collWebsites = oSiteCollection.AllWebs;
foreach (SPWeb oWebsite in collWebsites)
{
SPFolderCollection collFolders = oWebsite.Folders;
foreach (SPFolder oFolder in collFolders)
{
foreach (SPFile file in oFolder.Files)
{
// Your code here
}
}
}

Categories