问题
I am running an SSIS Package that contains a C# Script which formats an Excel File on Windows Server 2012 R2.
When I run the package it gives me this error Microsoft Office Excel cannot access the file '\\FolderPath\FilePath'
I have seen this question Microsoft Office Excel cannot access the file 'c:\inetpub\wwwroot\Timesheet\App_Data\Template.xlsx' and have checked my permission's and they are correct.
I also tried to add Double Quotes around the final FilePath like this sFile = "\"" + sFile + "\"";
but this outputs the error Microsoft Excel cannot access the file '"\FolderPath\FilePath" it is removing one \ I really don't understand why.
Below is the original code
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Reflection;
public int Main()
{
StringBuilder sb = new StringBuilder();
string LogFilePath = "\\\\LogFilePath";
string strExcelDataOutPut = "\\\\FolderPath";
string sPath = "\\filePath";
try {
FormatFile(strExcelDataOutPut,sPath);
} catch (Exception ex) {
using (System.IO.StreamWriter outfile = new System.IO.StreamWriter(LogFilePath))
{
sb.AppendLine("Error Occured ..Please see the error Message :" + ex.Message);
outfile.Write(sb.ToString());
}
}
}
public void FormatFile(string strExcelDataOutPut, string sPath)
{
Microsoft.Office.Interop.Excel.Application objExcelApp = new Excel.Application();
Microsoft.Office.Interop.Excel.Workbook objExcelWbk = default(Excel.Workbook);
Microsoft.Office.Interop.Excel.Worksheet objWrksheet = default(Excel.Worksheet);
object missing = Missing.Value;
Excel.Range crange1;
string sFile = string.Empty;
string sWorkSheet = string.Empty;
//--Month in English/French
string sMonthYear = string.Empty;
try
{
objExcelApp.DisplayAlerts = false;
objExcelApp.Visible = false;
sFile = strExcelDataOutPut + sPath;
//--Check if the file exists ---------------------------------------------------------
if (System.IO.File.Exists(sFile))
{
sWorkSheet = "Sheet1";
}
objExcelWbk = objExcelApp.Workbooks.Open(sFile.Trim(), missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, true);
objWrksheet = (Excel.Worksheet)objExcelWbk.Worksheets[sWorkSheet];
((Microsoft.Office.Interop.Excel._Worksheet)objWrksheet).Activate();
//--Format
sMonthYear = "Report as at: " + DateTime.Today.ToString("MMMM") + " " + DateTime.Today.Day.ToString() + ", " + DateTime.Today.Year.ToString();
objWrksheet.PageSetup.LeftHeader = "&8&F";
//objWrksheet.PageSetup.CenterFooter = @"&12&""Arial,Bold" + sMonthYear;
objWrksheet.PageSetup.CenterFooter = " " + sMonthYear;
crange1 = (Excel.Range)objWrksheet.Cells[1, 1];
crange1.Select();
//objExcelWbk.SaveAs(sFile, missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive, missing, missing, missing, missing, missing);
//objExcelWbk.Close(true, missing, missing);
objExcelWbk.Save();
objExcelWbk.Close(true, sFile, missing);
objExcelApp.Quit();
}
catch
{
throw;
}
finally
{
objWrksheet = null;
objExcelWbk = null;
objExcelApp = null;
System.GC.Collect();
}
}
回答1:
So I came across this Microsoft Blog. Although this is very weird that why you need to create the Desktop folders but it works. In case the link gets removed see the Answer Below
Resolution ************ ·
A “Desktop” folder seems to be necessary in the “systemprofile” folder in the location C:\Windows\SysWOW64\config\ to open an Excel file ·
Create the “Desktop” folder for Windows 2008 Server (x64) under the location C:\Windows\SysWOW64\config\systemprofile ·
And for a 32 bit Windows 2008 Server create the “Desktop” folder under the location C:\Windows\System32\config\systemprofile ·
After creating the folder the SQL Server jobs should execute successfully
回答2:
The code feels like it's been too sanitized for the internet as it doesn't seem right with regard to your file work.
C# observations
Escaping a slash is downright painful due it also signalling a control character. C# allows you indicate a string is a verbatim string and then you don't have to deal with escaping everything.
In your Main, you have
string strExcelDataOutPut = "\\\\FolderPath";
string sPath = "\\filePath";
When those strings are evaluated, you'd have values of \\FolderPath
and \filePath
You combine them within the method call as
sFile = strExcelDataOutPut + sPath;
which generates an UNC path as \\FolderPath\filePath
which is fine, but that's not a file name. It seems that you're missing the actual file name, like
sFile = strExcelDataOutPut + sPath + @"\Something.xlsx";
I have a trick for you on combining paths to make a valid path - there's a library and method for that. System.IO.Path.Combine It's doubly nice as you don't have to do as much escaping for the values there.
sFile = Path.Combine(strExcelDataOutPut, sPath, "Something.xlsx");
Your check for file existence sets the worksheet name if the file is found but does nothing if the file isn't found. Isn't that going to cause issues when you attempt to open the file that doesn't exist? Or if Excel will create the file, then when you attempt to access a worksheet named (empty string) in the Worksheets collection, that's going to fail.
Finally, beware of using the double dots in your method there.
SSIS observations
You don't specify where you're running this package and encountering issues but I can see a number of things that can go wrong.
The first and foremost is licensing. As long as you, your DBAs and management understand that this implementation is going to require that you purchase a Microsoft Office license for your SQL Server instance(s). Office requires more frequent patching with potential reboots, than your typical server level products have. This means if your SQL Server installations have a hard SLA, this implementation might encroach on that window.
215 dollars out the door (as quoted by CDW for a home and business license) and our servers now get patched more frequently. Awesome, now the server FolderPath dies, or the file share changes or anything happens and now you need to change that path - you're looking at opening the package up, editing the script value, saving it, checking it into version control (you do use version control, right?), and then submitting that into whatever change control process your employer has. That's a lot of wasted time in my world.
Instead, you can make
string LogFilePath = "\\\\LogFilePath";
string strExcelDataOutPut = "\\\\FolderPath";
string sPath = "\\filePath";
into something like
string LogFilePath = Dts.Variables["LogPath"].Value.ToString();
string strExcelDataOutPut = Dts.Variables["ExcelPath"].Value.ToString();
string sPath = Dts.Variables["FilePath"].Value.ToString();
What's that get you? Now you're running your script based on Variables within SSIS. You can make a change to those variables, either through Configuration or Parameterization (depending on deployment model) and then your package behaviour changes without code changes. You're updating an external value in some repository instead of code itself. In the environments I work in, that's generally a much lower risk change to implement versus a code change (even if all you did was change the value of a variable).
Unless you have a process already built out for consuming your custom logging there, stop and use the native tooling. Raise an Information, Warning or Error message. Otherwise, you're going to need to ensure SSIS can write to that location (more on this later).
Dts.Events.FireError(0, "Excel Writer", "Error Occurred...Please see the error Message :" + ex.Message, String.Empty, 0);
Now when your package is running, that information will be dumped to the Error handler. If you're using the Project Deployment Model, that information will automatically be logged to the SSISDB.catalog.operation_messages table and shows up in your friendly reports. Otherwise, go configure logging and SSIS will write those events to a table, flat file, event viewer, profiler and/or an XML file. The important thing is you don't reinvent the wheel unless you have a really good reason.
When you're running this SSIS package, you'll need to ensure you are using the correct version of dtexec relative to your 32 or 64 bit version of Office. This bites many people in the backside. Office generally defaults to a 32 bit installation, which is a pity.
When this package is operationalized, i.e. it is running on SQL Server, probably as part of a SQL Agent job, the Account that runs SQL Agent will likely need a higher level of permissions that is generally granted to the account. It'll need network access to wherever that file is shared to. It will need InteractWithDesktop to be able to instantiate an instance of Excel and then do the needful. The SQL Agent job step will need to ensure that it has correctly flagged the SSIS package to be run as 32 bit instead of 64 (default). If you didn't implement logging and you're not using the Project Deployment Model, then the package generates an error, the error message will be captured within the SQL Agent job history. That'll be useful at some point.
来源:https://stackoverflow.com/questions/37868405/microsoft-excel-cannot-access-the-file-on-windows-server-2012