Closedxml: can't download file and immediately send it via mail - c#

I want to download an excel file and automatically send the file to a mailadress aswell. Downloading the file works, but when I include the code for sending mails, it throws a FileNotFoundException: 'Could not find file 'C:\Users\User\source\repos\Project\Taijitan\Taijitan\O soto gari.xlsx'.'.
I noticed that when downloading the file, it automatically goes to the Downloads folder on my pc. When I uncomment the mail code, it doesn't seem to download, instead it just loads before throwing the error I mentioned above. I tried using Path.GetFullPath(name) to find the excel file so that I don't have to hardcode any paths (in case any of my colleagues have a different path), but it doesn't seem to work.
Controller
public IActionResult GenerateList(int id)
{
Lesson lesson = _lessonRepository.GetBy(id);
var download = lesson.GetCommentsList();
string file = lesson.Name + ".xlsx";
MailSender.SendListMail(lesson.Name, file);
return download;
}
Generate excel + download
public List<Comment> Comments { get; set; }
public IActionResult GetCommentsList()
{
List<string> list = new List<string>();
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add("Comments");
foreach (var comment in Comments)
{
list.Add(comment.Body);
}
string name = Name + "comment";
ws.Cell(1, 1).Value = name;
ws.Range(1, 1, 1, 1).AddToNamed("Titles");
var CommentsRange = ws.Cell(2, 1).InsertData(list.AsEnumerable());
var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true;
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
titlesStyle.Fill.BackgroundColor = XLColor.Blue;
titlesStyle.Font.FontColor = XLColor.White;
wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle;
ws.Columns().AdjustToContents();
wb.SaveAs(name + ".xlsx");
using (var stream = new MemoryStream())
{
wb.SaveAs(stream);
stream.Flush();
return new FileContentResult(stream.ToArray(),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = name + ".xlsx"
};
}
}
MailSender
public static void SendListMail(string list, string file)
{
MailAddress Sender = new MailAddress("SenderEmail#gmail.com", "CommentsSender");
MailAddress Receiver = new MailAddress("ReceiverEmail#gmail.com", "Comments inbox");
const string SenderPassword = "password123";
string Attachment = Path.GetFullPath(file);
SmtpClient smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(Sender.Address, SenderPassword),
Timeout = 30000
};
using (MailMessage message = new MailMessage(Sender, Receiver)
{
Subject = list,
Body = "List in attachment",
Attachments = {new Attachment(Attachment)}
})
{
smtp.Send(message);
}
}
I'm trying to figure out where it goes wrong and how I can solve this.
My guess is it has something to do with synchronization (don't send the mail until the download is complete) and Path.GetFullPath() not checking outside the solutionfolder.
EDIT
Just noticed that when I try to send an email without the attachment, it sends the mail, but doesn't download anything, and the loading icon in the tab keeps spinning.
EDIT 2 Figured out the first problem, the placement of the return statement in the download method caused a loop.
Now I'm still having the could not find file error.

Problem turned out to be a dumb mistake. File got saved with the value of 'name' (file.Name + "comment.xlsx", but the download/email sender was looking for file.Name + ".xlsx" (didn't have "comment" in its name)

Related

What type of variable am I passing to the API controller while uploading the file?

Here is the complete code on how to send emails WITH ATTACHMENTS via API.
I had problem with deleting the file after sending it to smtp server. And I couldn't figure out how to pass additional data along with the files.
This article helped https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2
Thanks you for help.
var formData = new FormData();
formData.append("to", "g#gmail.com");
formData.append("subject", "Yo man");
formData.append("body", "I'm happy");
formData.append("file[]", $('#fileToAttach')[0].files[0]);
$.ajax({
url: "/api/emailwithattachment",
type: 'POST',
data: formData,
processData: false,
contentType: false,
})
[HttpPost]
[AllowAnonymous]
[Route("api/emailwithattachment")]
public async Task<string> UploadFile()
{
var ctx = HttpContext.Current;
var root = ctx.Server
.MapPath("~/UploadedFiles");
var provider =
new MultipartFormDataStreamProvider(root);
await Request.Content
.ReadAsMultipartAsync(provider);
var To = "";
var Subject = "";
var Body = "";
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
if (key == "to")
To = val;
if (key == "subject")
Subject = val;
if (key == "body")
Body = val;
}
}
MailMessage mail = new MailMessage();
SmtpClient SmtpServer = new SmtpClient("smtp.gmail.com", 587);
mail.From = new MailAddress("g#gmail.com");
mail.To.Add(To);
mail.Subject = Subject;
mail.Body = Body;
SmtpServer.Credentials = new NetworkCredential("g#gmail.com", "password");
SmtpServer.EnableSsl = true;
System.Net.Mail.Attachment attachment;
List<string> filePaths = new List<string>();
foreach (var file in provider.FileData)
{
var fileName = "CONF " + " " + DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") + ".pdf";
var localFileName = file.LocalFileName;
var filePath = Path.Combine(root, fileName);
File.Move(localFileName, filePath);
attachment = new System.Net.Mail.Attachment(filePath);
mail.Attachments.Add(attachment);
filePaths.Add(filePath);
}
SmtpServer.Send(mail);
mail.Dispose();/////Here was the problem
foreach (var file in filePaths)
{
try
{
Console.WriteLine(file);
File.Delete(file);
}
catch (Exception e)
{
return e.Message;
}
}
return "success";
}
You need to figure out wich other process is using the file. And then close the process, or at least get it to release the file. However the 90% case for this exception, is that you opened the file in this programm before and did not properly release it.
File and Networking classes involve unmanaged resources - OS Level handles. Their classes are marked as Disposeable. They need to be properly disposed off - after use - to properly release those unmanaged resources. My rule for Disposeable classes of all shapes and forms is: "Create. Use. Dispose. All in the same piece of code, ideally using a using statement."

Exception during delete file attached to a sent email

After successfully sending email with attachment I have to delete files I've sent as attachement.
Files are in use so I have an exception.
I've used the code of the documentation. I'm using a method to create and send the email so everything is disposed automatically after calling it.
MimeMessage eMail = new MimeMessage();
eMail.From.Add (new MailboxAddress(fromDescription, fromAddress));
foreach (string to in toAddress)
eMail.To.Add(new MailboxAddress(to));
if (ccAddress != null)
foreach (string cc in ccAddress)
eMail.Cc.Add(new MailboxAddress(cc));
if (ccnAddress != null)
foreach (string ccn in ccnAddress)
eMail.Bcc.Add(new MailboxAddress(ccn));
eMail.Subject = subject;
var Body = new TextPart("plain")
{
Text = body
};
// now create the multipart/mixed container to hold the message text and the attachment
var multipart = new Multipart("mixed");
multipart.Add(Body);
if (attachments != null)
{
foreach (string attachmentPath in attachments)
{
// create an attachment for the file located at path
var attachment = new MimePart(MimeTypes.GetMimeType(attachmentPath))
{
Content = new MimeContent(File.OpenRead(attachmentPath), ContentEncoding.Default),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(attachmentPath)
};
multipart.Add(attachment);
}
}
// now set the multipart/mixed as the message body
eMail.Body = multipart;
using (var client = new SmtpClient())
{
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
//client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(SmtpHost, SmtpPort, SecureSocketOptions.SslOnConnect);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
//client.AuthenticationMechanisms.Remove("XOAUTH2");
// Note: only needed if the SMTP server requires authentication
client.Authenticate(SmtpUser, SmtpPassword);
client.Send(eMail);
client.Disconnect(true);
}
What's wrong? Anyoone could help me?
Thank you
You need to dispose the stream you opened for the attachment:
File.OpenRead(attachmentPath)
You could do something like this:
var streams = new List<Stream> ();
if (attachments != null) {
foreach (string attachmentPath in attachments) {
// create an attachment for the file located at path
var stream = File.OpenRead(attachmentPath);
var attachment = new MimePart(MimeTypes.GetMimeType(attachmentPath)) {
Content = new MimeContent(stream, ContentEncoding.Default),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(attachmentPath)
};
multipart.Add(attachment);
streams.Add (stream);
}
}
And then, after sending the message, do this:
foreach (var stream in streams)
stream.Dispose ();

ASP.NET MVC hosted in GoDaddy

I've been working on a ASP.NET MVC website and It's been hosted in GoDaddy. You can directly send an inquire form to a specific email in the website I'm making, It's working perfectly fine, the form is sending without a problem, until I hosted it. Whenever I try to send a form, I keep getting this error:
System.IO.DirectoryNotFoundException: Could not find a part of the
path
'G:\PleskVhosts\sellurs.com\httpdocs\App_Data\uploads\SOS.docx'
But when I try to run my project that is not hosted, the form is still sending without a problem. Why is that? Here's my controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Index(EmailFormModel model, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
List<string> paths = new List<string>();
foreach (var file in files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
paths.Add(path);
}
}
var message = new MailMessage();
foreach (var path in paths)
{
var fileInfo = new FileInfo(path);
var memoryStream = new MemoryStream();
using (var stream = fileInfo.OpenRead())
{
stream.CopyTo(memoryStream);
}
memoryStream.Position = 0;
string fileName = fileInfo.Name;
message.Attachments.Add(new Attachment(memoryStream, fileName));
}
//Rest of business logic here
string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = (ReCaptcha.Validate(EncodedResponse) == "True" ? true : false);
if (IsCaptchaValid)
{
var body = "<p><b>Email From:</b> {0} ({1})</p><p><b>Subject:</b> {2} </p><p><b>Message:</b></p><p>{3}</p><p><b>Description:</b></p><p>{4}</p>";
message.To.Add(new MailAddress("***")); // replace with valid value
message.From = new MailAddress("***"); // replace with valid value
message.Subject = "(Inquire)";
message.Body = string.Format(body, model.FromName, model.FromEmail, model.FromSubject, model.Message, model.Desc);
message.IsBodyHtml = true;
using (var smtp = new SmtpClient())
{
var credential = new NetworkCredential
{
UserName = "***", // replace with valid value
Password = "***" // replace with valid value
};
smtp.Credentials = credential;
smtp.Host = "smtp.live.com";
smtp.Port = 587;
smtp.EnableSsl = true;
smtp.SendCompleted += (s, e) =>
{
//delete attached files
foreach (var path in paths)
System.IO.File.Delete(path);
};
await smtp.SendMailAsync(message);
ViewBag.Message = "Your message has been sent!";
ModelState.Clear();
return View("Index");
}
} else
{
TempData["recaptcha"] = "Please verify that you are not a robot!";
}
} return View(model);
}
I think the problem is in the pathing, but I'm not really sure how to fix it. Someone please help me. I'm just new with this kind of stuffs. Thank you in advance. And also, when the website was hosted the navigation bar becomes blue, but in the local host it's black. And the background picture is not showing in the hosted website. Pleeease help me. Thank you.
this type of error occur in following case
case1: you haven't set permission to create file programmatically/by IIS so you have to change permission from your cpanel.
case2: file path may contain special character but in your case there is all plain text
Soln:
use file exist to check whether file is exist or not so you can figure out whats wrong going on. all the best....

Google Drive upload fails

I have a method that checks if my file exists on the drive and if not, it uploads one and adds permission.
This method worked while testing the first time and Update requests worked on the file to.
After deleting all existing files in my drive the upload stopped working, the responsebody is null and if I look at it in viddler the requestbody is also empty.
(the file is a valid docx)
I am using the following code:
DriveService service = Google_SDK.Authentication.AppFlowMetadata.BuildServerDriveService();
Google.Apis.Drive.v2.Data.File file;
var items = service.Files.List().Execute().Items.ToLookup(a => a.OriginalFilename = doc.Name + " (" + doc.Id + ".docx)");
if (items.Count > 0)
{
// This worked while testing:
//string fileId = items.FirstOrDefault().FirstOrDefault().Id;
//FilesResource.UpdateRequest uploadRequest = service.Files.(body, fileId, stream, "application/vnd.google-apps.document");
//uploadRequest.Convert = true;
//uploadRequest.Upload();
file = items.FirstOrDefault().FirstOrDefault();
}
else
{
Google.Apis.Drive.v2.Data.File body = new Google.Apis.Drive.v2.Data.File();
body.Title = doc.Name + " (" + doc.Id + ".docx)";
body.OriginalFilename = doc.Name + " (" + doc.Id + ".docx)";
body.Editable = true;
body.Shared = false;
body.WritersCanShare = false;
body.Copyable = false;
body.Description = doc.Notes;
body.MimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
byte[] byteArray = doc.DocxContent; // valid byteArray
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
FilesResource.InsertMediaUpload uploadRequest = service.Files.Insert(body, stream, "application/vnd.google-apps.document");
uploadRequest.Convert = true;
uploadRequest.Upload();
file = uploadRequest.ResponseBody; // No errors or exceptions but returns null
PermissionsResource.InsertRequest permissionRequest = service.Permissions.Insert(new Permission()
{
Role = "writer",
Type = "user",
EmailAddress = "user#domain.com",
Value = "user#domain.com"
}, file.Id); // file.Id fails becase file equals null
permissionRequest.Execute();
}
I really do not see what I am doing wrong and why this worked the first time.
Thank you in advance.
Edit:
I've been debugging some other parts of my project and I found out that the changes still exist, this seems logical that any deletion is saved as a change for version controll.
Maybe that has something to do with my problems?
Also I forgot to mention that the deletion of all the files is done by code and the drive used is not a gmail account but an *.apps.googleusercontent.com account.
The code I used to remove all the files:
DriveService service = Google_SDK.Authentication.DriveServiceProvider.Service;
FilesResource.ListRequest listRequest = service.Files.List();
FileList listResponse = listRequest.Execute();
int amount = listResponse.Items.Count();
foreach(Google.Apis.Drive.v2.Data.File f in listResponse.Items.AsQueryable()){
FilesResource.DeleteRequest deleteRequest = service.Files.Delete(f.Id);
deleteRequest.Execute();
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(String.Format("Removed {0} items.",amount));
return response;
Edit 2:
I've created a completely new document in my system and tried uploading it to the google drive. This was succesfull! After deleting the new document from the drive I got the same behavior as described above, an empty responsebody while uploading.
So even though I removed the old document it still recognizes that it is the same document that used to be on the drive.
Does somebody know a way to prevent this?

file in use after sending email with attachment

I am sending a file through .net's smtp as such:
private void SendMail()
{
var _msg = new MailMessage();
const string cEmailUsername = "xxxxxx";
const string cEmailPassword = "zzzzzz";
const string cSmtpClient = "yyyyyyy";
try
{
using (var smtp = new SmtpClient(cSmtpClient))
{
_msg.From = string.IsNullOrEmpty(Configuration.CompanyEmailAddress)
? new MailAddress(cEmailUsername)
: new MailAddress(Configuration.CompanyEmailAddress.ToLower());
_msg.Subject = string.Format("Request for .Net Support - {0} - {1}", Configuration.CompanyID,
Configuration.GetCompanyName());
_msg.Body = string.Format("Propane.exe date: {0}{1}{1}{2}", Configuration.VersionDate, Environment.NewLine,
"Check parsing log file attached.");
_msg.To.Add("eric#suburbansoftware.com");
_msg.Attachments.Add(new Attachment(_logFile));
var cred2 = new NetworkCredential(cEmailUsername, cEmailPassword);
smtp.Port = 587;
smtp.Credentials = cred2;
smtp.Timeout = 0;
smtp.Send(_msg);
}
var oldfile = _logFile + ".old";
if (File.Exists(oldfile))
{
File.Delete(oldfile);
}
File.Move(_logFile,oldfile);
}
catch (Exception ex)
{
richTextBox_Message.Text += ex.Message;
}
}
I am getting a file in use exception when I get to the file.move. If I comment out the using portion, the move works just fine.
Is there a way around this or am I doing it wrong?
Using works just with smtp, not with _msg. This variable is the one containing the attachment and is not disposed. Include this code right after finishing the using part and everything should be fine:
_msg.Dispose();
_msg = null;

Categories

Resources