I need to programmatically open an Excel file that is stored in a MOSS 2007 Shared Documents List. I’d like to use an OleDbConnection so that I may return the contents of the file as a DataTable. I believe this is possile since a number of articles on the Web imply this is possible. Currently my code fails when trying to initialize a new connection (oledbConn = new OleDbConnection(_connStringName); The error message is:
Format of the initialization string does not conform to specification starting at index 0.
I believe I am just not able to figure the right path to the file. Here is my code:
public DataTable GetData(string fileName, string workSheetName, string filePath)
{
// filePath == C:\inetpub\wwwroot\wss\VirtualDirectories\80\MySpWebAppName\Shared Documents\FY12_FHP_SPREADSHEET.xlsx
// Initialize global vars
_connStringName = DataSource.Conn_Excel(fileName, filePath).ToString();
_workSheetName = workSheetName;
dt = new DataTable();
//Create the connection object
if (!string.IsNullOrEmpty(_connStringName))
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
oledbConn = new OleDbConnection(_connStringName);
try
{
oledbConn.Open();
//Create OleDbCommand obj and select data from worksheet GrandTotals
OleDbCommand cmd = new OleDbCommand("SELECT * FROM " + _workSheetName + ";", oledbConn);
//create new OleDbDataAdapter
OleDbDataAdapter oleda = new OleDbDataAdapter();
oleda.SelectCommand = cmd;
oleda.Fill(dt);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
oledbConn.Close();
}
});
}
return dt;
}
public static OleDbConnection Conn_Excel(string ExcelFileName, string filePath)
{
// filePath == C:\inetpub\wwwroot\wss\VirtualDirectories\80\MySpWebAppName\Shared Documents\FY12_FHP_SPREADSHEET.xlsx
OleDbConnection myConn = new OleDbConnection();
myConn.ConnectionString = string.Format(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=Excel 12.0");
return myConn;
}
What am I doing wrong, or is there a better way to get the Excel file contents as a DataTable?
I ended up using the open source project Excel Data Reader
Related
I created a simple action to download some content as excel file:
public FileResult ExportToExcel()
{
string filename = "list.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
List<string[]> list = new List<string[]>();
list.Add(new[] { "col1", "col2", "cols3" });
list.Add(new[] { "col4", "col5", "cols6" });
list.Add(new[] { "col7", "col8", "cols9" });
StringWriter sw = new StringWriter();
sw.WriteLine("ID,Date,Description");
foreach (string[] item in list)
{
sw.WriteLine("{0},{1},{2}", item[0], item[1], item[2]);
}
byte[] fileContents = Encoding.UTF8.GetBytes(sw.ToString());
return this.File(fileContents, contentType, filename);
}
I have 2 issues with it:
1. The file is downloaded but I cannot open it and am getting a warning:
Excel cannot open the file ... because the file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.
When I use old excel format:
string filename = "List.xls";
string contentType = "application/vnd.ms-excel";
I am able to open the file but after 3 different warnings about file being corrupted etc.
Btw I compared saving and tried to write file as pdf
string filename = "List.pdf";
string contentType = "application/pdf";
And I still couldn't open the file - it said format is not valid etc.
2. The contents appear in the file in the second example however the commas are not recognised as column separators and all data in a row is written as one column.
What separator to use for excel format or how to write data to file to have it in a table excel format?
Ideal solution for me would be just return exported view (strongly typed) but I didn't find out how to do it so far.
--- EDIT: Working solution ---
public FileResult ExportToExcel()
{
string filename = "List.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
List<string[]> titles = new List<string[]>() { new[] { "a", "be", "ce" } };
List<string[]> list = new List<string[]>
{
new[] { "col1", "col2", "cols3" },
new[] { "col4", "col5", "cols6" },
new[] { "col7", "col8", "cols9" },
new[] { "col10", "col11", "cols12" }
};
XLWorkbook wb = new XLWorkbook();
XLTables xt = new XLTables();
var ws = wb.Worksheets.Add("List");
ws.Cell(1, 1).InsertData(titles);
ws.Cell(2, 1).InsertData(list);
ws.Columns().AdjustToContents();
var stream = new MemoryStream();
wb.SaveAs(stream);
stream.Seek(0, SeekOrigin.Begin);
wb.Dispose();
return this.File(stream, contentType, filename);
}
The reason why it is not being correctly rendered is because you cannot just return the mime type and expect the framework to figure out the rest.
I would go with a nuget package called closedXML which will allow you to create an excel file in memory and stream it back to the client.
it comes with a full documentation (here) for more information.
Using this package you can do something like
XLWorkbook wb = new XLWorkbook();
XLTables xt = new XLTables();
var ws = wb.Worksheets.Add("Sheet 1");
var firstCell = ws.Cell(1, 1);
var lastCell = ws.Cell(3, list.Count);
var table = ws.Range(firstCell.Address, lastCell.Address).AsTable();
table.Cell(2, 1).InsertData(list);
table.CreateTable();
ws.Columns().AdjustToContents();
using(var stream = new MemoryStream())
{
wb.SaveAs(stream);
stream.Seek(0, SeekOrigin.Begin);
wb.Dispose();
return File(stream , contentType, filename);
}
I have 2 files saved on Azure blob storage:
Abc.txt
Pqr.docx
Now i want to create zip files of this 2 files and allow user to download.
I have saved this in my database table field like this:
Document
Abc,Pqr
Now when i click on download then i am getting file like below with no data in it and file extension are lost too like below:
I want user to get exact file(.txt,.docx) in zip when user download zip file.
This is my code:
public ActionResult DownloadImagefilesAsZip()
{
string documentUrl = repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
if (!string.IsNullOrEmpty(documentUrl))
{
string[] str = documentUrl.Split(',');
if (str.Length > 1)
{
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
if (!string.IsNullOrEmpty(t))
{
Stream s = this.GetFileContent(t);
zip.AddEntry("File" + cnt, s);
}
cnt++;
}
zip.Save(outputStream);
outputStream.Position = 0;
return File(outputStream, "application/zip", "all.zip");
}
}
}
public Stream GetFileContent(string fileName)
{
CloudBlobContainer container = this.GetCloudBlobContainer();
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
var stream = new MemoryStream();
blockBlob.DownloadToStream(stream);
return stream;
}
public CloudBlobContainer GetCloudBlobContainer()
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"].ToString());
CloudBlobClient blobclient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer blobcontainer = blobclient.GetContainerReference("Mystorage");
if (blobcontainer.CreateIfNotExists())
{
blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
}
blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
return blobcontainer;
}
I want same file to be downloaded when user download zip file.
Can anybody help me with this??
I'm not a web dev, but hopefully this will help. This snippet of code is in a method where I download a list of blobs into a zip file archive using a stream. The list of files had the slashes in all directions, so there's code in here to fix this, and to make sure I'm getting the blob reference with the right text (no URL, and no opening slash if the blob is in a "folder").
I suspect your problem is not using a memory stream or a binary writer. Specificity helps sometimes. Good luck.
using (ZipArchive zipFile = ZipFile.Open(outputZipFileName, ZipArchiveMode.Create))
{
foreach (string oneFile in listOfFiles)
{
//Need the filename, complete with relative path. Make it like a file name on disk, with backwards slashes.
//Also must be relative, so can't start with a slash. Remove if found.
string filenameInArchive = oneFile.Replace(#"/", #"\");
if (filenameInArchive.Substring(0, 1) == #"\")
filenameInArchive = filenameInArchive.Substring(1, filenameInArchive.Length - 1);
//blob needs slashes in opposite direction
string blobFile = oneFile.Replace(#"\", #"/");
//take first slash off of the (folder + file name) to access it directly in blob storage
if (blobFile.Substring(0, 1) == #"/")
blobFile = oneFile.Substring(1, oneFile.Length - 1);
var cloudBlockBlob = this.BlobStorageSource.GetBlobRef(blobFile);
if (!cloudBlockBlob.Exists()) //checking just in case
{
//go to the next file
//should probably trace log this
//add the file name with the fixed slashes rather than the raw, messed-up one
// so anyone looking at the list of files not found doesn't think it's because
// the slashes are different
filesNotFound.Add(blobFile);
}
else
{
//blob listing has files with forward slashes; that's what the zip file requires
//also, first character should not be a slash (removed it above)
ZipArchiveEntry newEntry = zipFile.CreateEntry(filenameInArchive, CompressionLevel.Optimal);
using (MemoryStream ms = new MemoryStream())
{
//download the blob to a memory stream
cloudBlockBlob.DownloadToStream(ms);
//write to the newEntry using a BinaryWriter and copying it 4k at a time
using (BinaryWriter entry = new BinaryWriter(newEntry.Open()))
{
//reset the memory stream's position to 0 and copy it to the zip stream in 4k chunks
//this keeps the process from taking up a ton of memory
ms.Position = 0;
byte[] buffer = new byte[4096];
bool copying = true;
while (copying)
{
int bytesRead = ms.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
entry.Write(buffer, 0, bytesRead);
}
else
{
entry.Flush();
copying = false;
}
}
}//end using for BinaryWriter
}//end using for MemoryStream
}//if file exists in blob storage
}//end foreach file
} //end of using ZipFileArchive
There are two things I noticed:
Once you read the blob contents in stream, you are not resetting that stream's position to 0. Thus all files in your zip are of zero bytes.
When calling AddEntry, you may want to specify the name of the blob there instead of "File"+cnt.
Please look at the code below. It's a console app that creates the zip file and writes it on the local file system.
static void SaveBlobsToZip()
{
string[] str = new string[] { "CodePlex.png", "DocumentDB.png" };
var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("images");
using (var fs = new FileStream("D:\\output.zip", FileMode.Create))
{
fs.Position = 0;
using (var ms1 = new MemoryStream())
{
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
var ms = new MemoryStream();
container.GetBlockBlobReference(t).DownloadToStream(ms);
ms.Position = 0;//This was missing from your code
zip.AddEntry(t, ms);//You may want to give the name of the blob here.
cnt++;
}
zip.Save(ms1);
}
ms1.Position = 0;
ms1.CopyTo(fs);
}
}
}
UPDATE
Here's the code in the MVC application (though I am not sure it is the best code :) but it works). I modified your code a little bit.
public ActionResult DownloadImagefilesAsZip()
{
string[] str = new string[] { "CodePlex.png", "DocumentDB.png" }; //repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
CloudBlobContainer blobcontainer = GetCloudBlobContainer();// azureStorageUtility.GetCloudBlobContainer();
MemoryStream ms1 = new MemoryStream();
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
var ms = new MemoryStream();
CloudBlockBlob blockBlob = blobcontainer.GetBlockBlobReference(t);
blockBlob.DownloadToStream(ms);
ms.Position = 0;//This was missing from your code
zip.AddEntry(t, ms);//You may want to give the name of the blob here.
cnt++;
}
zip.Save(ms1);
}
ms1.Position = 0;
return File(ms1, "application/zip", "all.zip");
}
I have seen people using ICSharpZip library, take a look at this piece of code
public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
{
using (var zipOutputStream = new ZipOutputStream(response.OutputStream))
{
zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
response.BufferOutput = false;
response.AddHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.ContentType = "application/octet-stream";
foreach (var file in files)
{
var entry = new ZipEntry(file.FilenameSlug())
{
DateTime = DateTime.Now,
Size = file.Filesize
};
zipOutputStream.PutNextEntry(entry);
storageService.ReadToStream(file, zipOutputStream);
response.Flush();
if (!response.IsClientConnected)
{
break;
}
}
zipOutputStream.Finish();
zipOutputStream.Close();
}
response.End();
}
Taken from here generate a Zip file from azure blob storage files
public String generateReport() {
try
{
final FacesContext facesContext = FacesContext.getCurrentInstance();
final HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.reset();
response.setHeader("Content-Disposition", "attachment; filename=\"" + "myReport.zip\";");
final BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
final ZipOutputStream zos = new ZipOutputStream(bos);
for (final PeriodScale periodScale : Scale.getPeriodScales(this.startDate, this.endDate))
{
final JasperPrint jasperPrint = JasperFillManager.fillReport(
this.reportsPath() + File.separator + "periodicScale.jasper",
this.parameters(this.reportsPath(), periodScale.getScale(),
periodScale.getStartDate(), periodScale.getEndDate()),
new JREmptyDataSource());
final byte[] bytes = JasperExportManager.exportReportToPdf(jasperPrint);
response.setContentLength(bytes.length);
final ZipEntry ze = new ZipEntry("periodicScale"+ periodScale.getStartDate() + ".pdf"); // periodicScale13032015.pdf for example
zos.putNextEntry(ze);
zos.write(bytes, 0, bytes.length);
zos.closeEntry();
}
zos.close();
facesContext.responseComplete();
}
catch (final Exception e)
{
e.printStackTrace();
}
return "";
}
This is my action method in the managedBean which is called by the user to print a JasperReport, but when I try to put more than one report inside the zip file it's not working.
getPeriodScales are returning two objects and JasperFillManager.fillReport is running correctly as the reports print when I just generate data for one report, when I try to stream two reports though and open in WinRar only one appears and I get an "unexpedted end of archive", in 7zip both appear but the second is corrupted.
What am I doing wrong or is there a way to stream multiple reports without zipping it?
I figured out what was, I was setting the contentLenght of the response with bytes.length size, but it should be bytes.length * Scale.getPeriodScales(this.startDate, this.endDate).size()
public JasperPrint generatePdf(long consumerNo) {
Consumer consumerByCustomerNo = consumerService.getConsumerByCustomerNo(consumerNo);
consumerList.add(consumerByCustomerNo);
BillHeaderIPOP billHeaderByConsumerNo = billHeaderService.getBillHeaderByConsumerNo(consumerNo);
Long billNo = billHeaderByConsumerNo.getBillNo();
List<BillLineItem> billLineItemByBilNo = billLineItemService.getBillLineItemByBilNo(billNo);
System.out.println(billLineItemByBilNo);
List<BillReadingLine> billReadingLineByBillNo = billReadingLineService.getBillReadingLineByBillNo(billNo);
File jrxmlFile = ResourceUtils.getFile("classpath:demo.jrxml");
JasperReport jasperReport = JasperCompileManager.compileReport(jrxmlFile.getAbsolutePath());
pdfContainer.setName(consumerByCustomerNo.getName());
pdfContainer.setTelephone(consumerByCustomerNo.getTelephone());
pdfContainer.setFromDate(billLineItemByBilNo.get(0).getStartDate());
pdfContainer.setToDate(billLineItemByBilNo.get(0).getEndDate());
pdfContainer.setSupplyAddress(consumerByCustomerNo.getSupplyAddress());
pdfContainer.setMeterNo(billReadingLineByBillNo.get(0).getMeterNo());
pdfContainer.setBillType(billHeaderByConsumerNo.getBillType());
pdfContainer.setReadingType(billReadingLineByBillNo.get(0).getReadingType());
pdfContainer.setLastBilledReadingInKWH(billReadingLineByBillNo.stream().filter(billReadingLine -> billReadingLine.getRegister().contains("KWH")).collect(Collectors.toList()).get(0).getLastBilledReading());
pdfContainer.setLastBilledReadingInKW(billReadingLineByBillNo.stream().filter(billReadingLine -> billReadingLine.getRegister().contains("KW")).collect(Collectors.toList()).get(0).getLastBilledReading());
pdfContainer.setReadingType(billReadingLineByBillNo.get(0).getReadingType());
pdfContainer.setRateCategory(billLineItemByBilNo.get(0).getRateCategory());
List<PdfContainer> pdfContainerList = new ArrayList<>();
pdfContainerList.add(pdfContainer);
Map<String, Object> parameters = new HashMap<>();
parameters.put("billLineItemByBilNo", billLineItemByBilNo);
parameters.put("billReadingLineByBillNo", billReadingLineByBillNo);
parameters.put("consumerList", consumerList);
parameters.put("pdfContainerList", pdfContainerList);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
return jasperPrint;
}
//above code is accroding to my requirement , you just focus on the jasperPrint object which am returning , then jasperPrint object is being used for pdf generation , storing those pdf into a zip file .
#GetMapping("/batchpdf/{rangeFrom}/{rangeTo}")
public String batchPdfBill(#PathVariable("rangeFrom") long rangeFrom, #PathVariable("rangeTo") long rangeTo) throws JRException, IOException {
consumerNosInRange = consumerService.consumerNoByRange(rangeFrom, rangeTo);
String zipFilePath = "C:\\Users\\Barada\\Downloads";
FileOutputStream fos = new FileOutputStream(zipFilePath +"\\"+ rangeFrom +"-To-"+ rangeTo +"--"+ Math.random() + ".zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream outputStream = new ZipOutputStream(bos);
try {
for (long consumerNo : consumerNosInRange) {
JasperPrint jasperPrint = generatePdf(consumerNo);
byte[] bytes = JasperExportManager.exportReportToPdf(jasperPrint);
outputStream.putNextEntry(new ZipEntry(consumerNo + ".pdf"));
outputStream.write(bytes, 0, bytes.length);
outputStream.closeEntry();
}
} finally {
outputStream.close();
}
return "All Bills PDF Generated.. Extract ZIP file get all Bills";
}
}
In my application first am allowing the user to create html document using CKEDITOR where user can can create html document and can insert image, form fields etc. the generated HTML document is than converted into PDF.
If HTML document contains plain text than PDF file gets created successfully but if user inserts image in it than gives error.
code for creating PDF document.
public ActionResult CreateFile(FormCollection data)
{
var filename = data["filename"];
var htmlContent = data["content"];
string sFilePath = Server.MapPath(_createdPDF + filename + ".html");
htmlContent = htmlContent.Trim();
if (!System.IO.File.Exists(sFilePath))
{
using (FileStream fs = new FileStream(sFilePath, FileMode.Create))
{
using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
{
w.Write(htmlContent);
}
}
createPDF(sFilePath);
}
return View();
}
private MemoryStream createPDF(string sFilePath)
{
string filename = Path.GetFileNameWithoutExtension(sFilePath);
string name = Server.MapPath(_createdPDF + filename + ".pdf");
MemoryStream ms = new MemoryStream();
TextReader tr = new StringReader(sFilePath);
Document document = new Document(PageSize.A4, 30, 30, 30, 30);
string urldir = Request.Url.GetLeftPart(UriPartial.Path);
urldir = urldir.Substring(0, urldir.LastIndexOf("/") + 1);
Response.Write(urldir);
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(name, FileMode.Create));
document.Open();
string htmlText = "";
StreamReader sr;
sr = System.IO.File.OpenText(sFilePath);
htmlText = sr.ReadToEnd();
sr.Close();
WebClient wc = new WebClient();
Response.Write(htmlText);
var props = new Dictionary<string, Object>();
props["img_baseurl"] = #"C:\Documents and Settings\shubham\My Documents\visdatemplatemanger\visdatemplatemanger\";
List<IElement> htmlarraylist = HTMLWorker.ParseToList(new StringReader(htmlText), null,props);
for (int k = 0; k < htmlarraylist.Count; k++)
{
document.Add((IElement)htmlarraylist[k]);
}
document.Close();
System.IO.File.Delete(sFilePath);
UploadURL(name);
return ms;
}
The error that i get if image is included in HTML document is:
Could not find a part of the path 'C:\Program Files\Common Files\Microsoft Shared\PDFimages\rectangle-shape.png'.
iTextSharp will try to resolve relative images for HTTP-based documents but ones served from the filesystem you need to either provide absolute paths or provide a base for it to search from.
//Image search base, path will be concatenated directly so make sure it contains a trailing slash
var props = new Dictionary<string, Object>();
props["img_baseurl"] = #"c:\images\";
//Include the props from above
htmlarraylist = HTMLWorker.ParseToList(sr, null, props);
I'm trying to port my pc XNA game to the xbox and have tried to implement xna easystorage alongside my existing pc file management for highscores. Basically trying to combine http://xnaessentials.com/tutorials/highscores.aspx/tutorials/highscores.aspx with http://easystorage.codeplex.com/
I'm running into one specific error regarding the LoadHighScores() as error with 'return (data);' - Use of unassigned local variable 'data'.
I presume this is due to async design of easystorage/xbox!? but not sure how to resolve - below are code samples:
ORIGINAL PC CODE: (works on PC)
public static HighScoreData LoadHighScores(string filename)
{
HighScoreData data; // Get the path of the save game
string fullpath = "Content/highscores.lst";
// Open the file
FileStream stream = File.Open(fullpath, FileMode.Open,FileAccess.Read);
try
{ // Read the data from the file
XmlSerializer serializer = new XmlSerializer(typeof(HighScoreData));
data = (HighScoreData)serializer.Deserialize(stream);
}
finally
{ // Close the file
stream.Close();
}
return (data);
}
XBOX PORT: (with error)
public static HighScoreData LoadHighScores(string container, string filename)
{
HighScoreData data;
if (Global.SaveDevice.FileExists(container, filename))
{
Global.SaveDevice.Load(container, filename, stream =>
{
File.Open(Global.fileName_options, FileMode.Open,//FileMode.OpenOrCreate,
FileAccess.Read);
try
{
// Read the data from the file
XmlSerializer serializer = new XmlSerializer(typeof(HighScoreData));
data = (HighScoreData)serializer.Deserialize(stream);
}
finally
{
// Close the file
stream.Close();
}
});
}
return (data);
}
Any ideas?
Assign data before return. ;)
data = (if_struct) ? new your_struct() : null;
if (Global.SaveDevice.FileExists(container, filename))
{
......
}
return (data);
}