1

I generate a PDF file, save it on the server:

var bytes = ms.ToArray();
. . .
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
. . .
File.WriteAllBytes(fileFullpath, bytes);

...and then save it into a Sharepoint Document Library, and send it as an email attachment to the person who generated the file:

SavePDFToDocumentLibrary(fileFullpath);
String from = GetFromEmailID();
String to = GetUserEmail();
String subjLine = String.Format("The PDF file you generated ({0})", pdfFileName);
String body = String.Format("The Direct Pay PDF file you generated ({0}) is attached.", pdfFileName);
SendEmailWithAttachment(fileFullpath, from, to, subjLine, body);
// Now that it has been put in a Document Library and emailed, delete the file that was saved locally
File.Delete(fileFullpath);

...at which point, I no longer need the file I saved to disk on the server, and so, as shown in the last line above, attempt to delete it.

However, it doesn't work. The now-redundant file is still in the place where it was saved.

Why, and how can I get it to understand that "Delete" really means "delete"?

UPDATE

Here are the methods Scott wanted to see:

// This works; got it from Henry Zucchini's answer at http://stackoverflow.com/questions/468469/how-do-you-upload-a-file-to-a-document-library-in-sharepoint
private void SavePDFToDocumentLibrary(String fullpath)
{
    String fileToUpload = fullpath;
    String sharePointSite = siteUrl;
    String documentLibraryName = "DirectPayPDFForms";

    using (SPSite oSite = new SPSite(sharePointSite))
    {
        using (SPWeb oWeb = oSite.OpenWeb())
        {
            if (!System.IO.File.Exists(fileToUpload))
                throw new FileNotFoundException("File not found.", fileToUpload);

            SPFolder doclib = oWeb.Folders[documentLibraryName];

            // Prepare to upload
            Boolean replaceExistingFiles = true;
            String fileName = System.IO.Path.GetFileName(fileToUpload);
            FileStream fileStream = File.OpenRead(fileToUpload);

            // Upload document
            SPFile spfile = doclib.Files.Add(fileName, fileStream, replaceExistingFiles);

            // Commit 
            doclib.Update();
        }
    }
}

// This is adapted from https://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage(v=vs.90).aspx
public static void SendEmailWithAttachment(string fileToMail, String from, String to, String subj, String body)
{
    String server = GetSMTPHostName(); //"468802-DEV-SPWF"; // change this to prod when go live, or programatically assign?
    // Specify the file to be attached and sent. 
    string file = fileToMail;
    // Create a message and set up the recipients.
    MailMessage message = new MailMessage(
       from,
       to,
       subj,
       body);

    // Create  the file attachment for this e-mail message.
    Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
    // Add time stamp information for the file.
    ContentDisposition disposition = data.ContentDisposition;
    disposition.CreationDate = System.IO.File.GetCreationTime(file);
    disposition.ModificationDate = System.IO.File.GetLastWriteTime(file);
    disposition.ReadDate = System.IO.File.GetLastAccessTime(file);
    // Add the file attachment to this e-mail message.
    message.Attachments.Add(data);

    //Send the message.
    SmtpClient client = new SmtpClient(server);
    // Add credentials if the SMTP server requires them.
    client.Credentials = CredentialCache.DefaultNetworkCredentials;

    try
    {
        client.Send(message);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception caught in CreateMessageWithAttachment(): {0}", ex.ToString());
    }
    // Display the values in the ContentDisposition for the attachment.
    // May not need/want this section
    ContentDisposition cd = data.ContentDisposition;
    Console.WriteLine("Content disposition");
    Console.WriteLine(cd.ToString());
    Console.WriteLine("File {0}", cd.FileName);
    Console.WriteLine("Size {0}", cd.Size);
    Console.WriteLine("Creation {0}", cd.CreationDate);
    Console.WriteLine("Modification {0}", cd.ModificationDate);
    Console.WriteLine("Read {0}", cd.ReadDate);
    Console.WriteLine("Inline {0}", cd.Inline);
    Console.WriteLine("Parameters: {0}", cd.Parameters.Count);
    foreach (DictionaryEntry d in cd.Parameters)
    {
        Console.WriteLine("{0} = {1}", d.Key, d.Value);
    }
    // </ May not need/want this section
    data.Dispose();
}

UPDATE 2

I see when stepping through it, after adding this test:

if (File.Exists(fileFullpath))
{
    File.Delete(fileFullpath);
}

...that there is an exception after all, in the IOException catch block:

The process cannot access the file 'C:\Users\TEMP.SP.018\Desktop\DirectPayDynamic_2015Jul28_19_02_clayshan_0.pdf' because it is being used by another process.

So how is one of the other methods holding onto it? ISTM that SavePDFToDocumentLibrary() is safe, because it uses using blocks.

Is data.Dispose(); in SendEmailWithAttachment() not enough? Do I need to explicitly call close there, or what?

UPDATE 3

I added "message.Dispose();" just prior to "data.Dispose();" in SendEmailWithAttachment(), but it made no difference.

B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 1
    You have a error either in `SavePDFToDocumentLibrary` or `SendEmailWithAttachment` which is not closing the file. You need to show the code for those two methods. Specificly the place you open a file using `fileFullpath` – Scott Chamberlain Jul 28 '15 at 21:53
  • @Mark: No error; it works fine, except for that one issue. Please see my Update with the two methods Scott requested. – B. Clay Shannon-B. Crow Raven Jul 28 '15 at 22:01
  • 1
    According to the [documentation](https://msdn.microsoft.com/en-us/library/system.io.file.delete(v=vs.110).aspx) it doesn't throw exception if the file doesn't exist. Put a File.Exists() check before the file is deleted just to see if it by any strange reason isn't there. – Christian Jul 28 '15 at 22:09
  • Aha! So there is an exception (as I see while stepping through it). Please see Update 2 – B. Clay Shannon-B. Crow Raven Jul 28 '15 at 22:20
  • 1
    You might want to dispose the `SmtpClient` and `MailMessage` as well (implicitly via `using` statement) – Christian Jul 28 '15 at 22:30
  • @Christian: SmtpClient doesn't seem to be a candidate for that; I tried it, and got 'System.Net.Mail.SmtpClient': type used in a using statement must be implicitly convertible to 'System.IDisposable' – B. Clay Shannon-B. Crow Raven Jul 28 '15 at 22:33
  • 1
    I've had this before and had to resort to a retry/sleep scenario - somehow the filesystem didn't immediately release the lock. If you didn't want to take the hit right them, you could create the files in a specific folder and delete all files in that folder that are over a minute old... both are ugly hacks when you code is just as it should work. – Mark Jul 28 '15 at 22:39
  • 1
    @B.ClayShannon What about `using` the `File.OpenRead` in `SavePDFToDocumentLibrary`? – Christian Jul 28 '15 at 22:43
  • @Christian: That did the trick; make it an answer, and I'll mark it as such. – B. Clay Shannon-B. Crow Raven Jul 28 '15 at 22:47

1 Answers1

1

Try disposing the file stream used in SavePDFToDocumentLibrary like so:

using (FileStream fileStream = File.OpenRead(fileToUpload))
{
    ...
}
Christian
  • 7,433
  • 4
  • 36
  • 61