SpreadsheetLight Updating excel file - c#

I would like to read the excel file then find the correct row and add a value to that row But I am getting a error message file is used by another process.
I have add the file to the FileStream so I am not sure why I am getting this error?
System.IO.IOException: 'The process cannot access the file 'D:\repos\FHIRVal\Update.xlsx' because it is being used by another process.'
public static int UpdateExecelFile(string id, string status)
{
string FilePath = "D:\\repos\\FHIRVal\\Update.xlsx";
using (SLDocument sl = new SLDocument())
{
FileStream fs = new FileStream(FilePath, FileMode.Open);
SLDocument sheet = new SLDocument(fs, "Sheet");
SLWorksheetStatistics stats = sheet.GetWorksheetStatistics();
for (int j = 1; j < stats.EndRowIndex; j++)
{
var value = sheet.GetCellValueAsString(j, 2);
if (value == id)
{
Console.WriteLine(string.Format("{0} --- {1}", "Updating File", id));
string updateRow = string.Format("{0}{1}", "C",j);
sl.SetCellValue(updateRow, status);
}
}
sheet.SaveAs(FilePath);
fs.Close();
}

I had the order incorrect I needed to have the fs.close before the sheetsaveas .

Related

Why am i receiving a "The process cannot access the file because it is being used by another process."

Im trying to process a set of files, i have a given number of txt files, which im currently joining into 1 txt file to apply filters to. The creation of the 1 file from multiple works great. But i have 2 questions and 1 error i cant seem to get around.
1 - Im getting an error when i try to read the newly created file so i can apply the filters. "The process cannot access the file because it is being used by another process."
2 - Am i approaching this the correct or more efficient way? by that i mean can the reading and filtering be applied before creating the concatenated file? I mean i still need to create a new file, but it would be nice to be able to apply everything before creating so that the file is already cleaned and ready for use outside the application.
Here is the current code that is having the issue and the 1 commented line that was my other attempt at releasing the file
private DataTable processFileData(string fname, string locs2 = "0", string effDate = "0", string items = "0")
{
DataTable dt = new DataTable();
string fullPath = fname;
try
{
using (StreamReader sr = new StreamReader(File.OpenRead(fullPath)))
//using (StreamReader sr = new StreamReader(File.Open(fullPath,FileMode.Open,FileAccess.Read, FileShare.Read)))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (!String.IsNullOrWhiteSpace(line))
{
string[] headers = line.ToUpper().Split('|');
while (dt.Columns.Count < headers.Length)
{
dt.Columns.Add();
}
string[] rows = line.ToUpper().Split('|');
DataRow dr = dt.NewRow();
for (int i = 0; i < rows.Count(); i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
}
//sr.Close();
sr.Dispose();
}
string cls = String.Format("Column6 NOT LIKE ('{0}')", String.Join("','", returnClass()));
dt.DefaultView.RowFilter = cls;
return dt;
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
return dt;
}
Here is the concatenation method:
private void Consolidate(string fileType)
{
string sourceFolder = #"H:\Merchant\Strategy\Signs\BACKUP TAG DATA\Wave 6\" + sfld;
string destinationFile = #"H:\Merchant\Strategy\Signs\BACKUP TAG DATA\Wave 6\" + sfld + #"\"+ sfld + #"_consolidation.txt";
// Specify wildcard search to match TXT files that will be combined
string[] filePaths = Directory.GetFiles(sourceFolder, fileType);
StreamWriter fileDest = new StreamWriter(destinationFile, true);
int i;
for (i = 0; i < filePaths.Length; i++)
{
string file = filePaths[i];
string[] lines = File.ReadAllLines(file);
if (i > 0)
{
lines = lines.Skip(1).ToArray(); // Skip header row for all but first file
}
foreach (string line in lines)
{
fileDest.WriteLine(line);
}
}
if (sfld == "CLR")
{
clrFilter(destinationFile);
}
if (sfld == "UPL")
{
uplFilter(destinationFile);
}
if (sfld == "HD")
{
hdFilter(destinationFile);
}
if (sfld == "PD")
{
pdFilter(destinationFile);
}
fileDest.Close();
fileDest.Dispose();
}
What im trying to accomplish is reading min(2 or 3 txt files and as much as 13 txt files) and applying some filtering. But im getting this error:
"The process cannot access the file because it is being used by another process."
You're disposing the stream reader with the following line
sr.Dispose();
Using a 'Using' statement will dispose after the stream goes out of context. So remove the Dispose line (if it wasn't clear below)

.Net Core: Reading data from CSV & Excel files

Using .net core & c# here.
I have a UI from which user can upload the Excel or CSV files. Once they upload this goes to my web api which handles the reading of the data from these files and returns json.
My Api code as:
[HttpPost("upload")]
public async Task<IActionResult> FileUpload(IFormFile file)
{
JArray data = new JArray();
using (ExcelPackage package = new ExcelPackage(file.OpenReadStream()))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
//Process, read from excel here and populate jarray
}
return Ok(data );
}
In my above code I am using EPPlus for reading the excel file. For excel file it works all fine but it cannot read csv file which is the limitation of EPPlus.
I searched and found another library CSVHelper: https://joshclose.github.io/CsvHelper/ The issue with this is it does vice versa and can read from CSV but not from Excel.
Is there any library available which supports reading from both.
Or would it be possible use EPPlus only but convert uploaded CSV to excel on the fly and then read. (please note I am not storing the excel file anywhere so cant use save as to save it as excel)
Any inputs please?
--Updated - Added code for reading data from excel---
int rowCount = worksheet.Dimension.End.Row;
int colCount = worksheet.Dimension.End.Column;
for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= colCount; col++)
{
var rowValue = worksheet.Cells[row, col].Value;
}
}
//With the code suggested in the answer rowcount is always 1
You can use EPPLus and a MemoryStream for opening csv files into an ExcelPackage without writing to a file. Below is an example. You may have to change some of the the parameters based on your CSV file specs.
[HttpPost("upload")]
public async Task<IActionResult> FileUpload(IFormFile file)
{
var result = string.Empty;
string worksheetsName = "data";
bool firstRowIsHeader = false;
var format = new ExcelTextFormat();
format.Delimiter = ',';
format.TextQualifier = '"';
using (var reader = new System.IO.StreamReader(file.OpenReadStream()))
using (ExcelPackage package = new ExcelPackage())
{
result = reader.ReadToEnd();
ExcelWorksheet worksheet =
package.Workbook.Worksheets.Add(worksheetsName);
worksheet.Cells["A1"].LoadFromText(result, format, OfficeOpenXml.Table.TableStyles.Medium27, firstRowIsHeader);
}
}
Here's using Aspose, which is unfortunately not free, but wow it works great. My API is using the streaming capability with Content-Type: multipart/form-data rather than the IFormFile implementation:
[HttpPut]
[DisableFormValueModelBinding]
public async Task<IActionResult> UploadSpreadsheet()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
return BadRequest($"Expected a multipart request, but got {Request.ContentType}");
}
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = (await reader.ReadNextSectionAsync()).AsFileSection();
//If you're doing CSV, you add this line:
LoadOptions loadOptions = new LoadOptions(LoadFormat.CSV);
var workbook = new Workbook(section.FileStream, loadOptions);
Cells cells = workbook.Worksheets[0].Cells;
var rows = cells.Rows.Cast<Row>().Where(x => !x.IsBlank);
//Do whatever else you want here
Please try with below code
private string uploadCSV(FileUpload fl)
{
string fileName = "";
serverLocation = Request.PhysicalApplicationPath + "ExcelFiles\\";
fileName = fl.PostedFile.FileName;
int FileSize = fl.PostedFile.ContentLength;
string contentType = fl.PostedFile.ContentType;
fl.PostedFile.SaveAs(serverLocation + fileName);
string rpath = string.Empty, dir = string.Empty;
HttpContext context = HttpContext.Current;
string baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.ApplicationPath.TrimEnd('/') + '/';
try
{
rpath = serverLocation + fileName;//Server.MapPath(dir + fileName);
using (Stream InputStream = fl.PostedFile.InputStream)
{
Object o = new object();
lock (o)
{
byte[] buffer = new byte[InputStream.Length];
InputStream.Read(buffer, 0, (int)InputStream.Length);
lock (o)
{
File.WriteAllBytes(rpath, buffer);
buffer = null;
}
InputStream.Close();
}
}
}
catch (Exception ex)
{
lblSOTargetVal.Text = ex.Message.ToString();
}
return rpath;
}
Use the Open XML SDK package and add insert working solution for it.

Reading xlsx file steam stuck with A disk error occurred during a write operation. (Exception from HRESULT: 0x8003001D (STG_E_WRITEFAULT))

i am using using OfficeOpenXml; for reading excel file and its working fine with other reading file but for one perticular file i got above mention error.
public ReadExcelFile(Stream stream, string worksheet, List<ExcelColumnMapping> columnMapping, ITypeConvert typeConvert = null, int headerSize = 1)
{
_stream = stream;
_excelPackage = new ExcelPackage(stream);
_worksheet = _excelPackage.Workbook.Worksheets[worksheet];
_headerSize = headerSize;
_rowCount = headerSize + 1;
var mapping = ColumnMappingRowNumber(columnMapping);
_resultColumnNumbersAndTypes = mapping.Select(x => Tuple.Create(x.Item2, x.Item3, x.Item4)).ToList();
_converter = typeConvert ?? new ExcelTypeConvert();
ReadLineValues();
}

write and read from byte stream

I have a page where the User can either upload their own csv or enter values into a listbox which then creates a csv (in the background). Regardless of which way the csv gets created I need to upload that csv to our server via a byte stream.
My problem is that when Im creating the csv I shouldn't have to create a temporary file, I should be able to write to the stream then read it back for uploading. How can I remove the need for the temporary file?
current code which works (but uses temp file):
try {
string filename = DateTime.Now.ToString("MMddyyHmssf");
filename = filename + ".csv";
string directory = ConfigurationManager.AppSettings["TempDirectory"].ToString();
path = Path.Combine(directory, filename);
using (StreamWriter sw = File.CreateText(path)) {
foreach (ListItem item in this.lstAddEmailAddress.Items) {
sw.WriteLine(" , ," + item.ToString());
}
}
} catch (Exception ex) {
string error = "Cannot create temp csv file used for importing users by email address. Filepath: " + path + ". FileException: " + ex.ToString();
this.writeToLogs(error, 1338);
}
}
// put here for testing the byte array being sent vs ready byte[] byteArray = System.IO.File.ReadAllBytes(path);
myCsvFileStream = File.OpenRead(path);
nFileLen = (int)myCsvFileStream.Length;
I have tried
Stream myCsvFileStream;
using (StreamWriter sw = new StreamWriter(myCsvFileStream)) {
foreach (ListItem item in this.lstAddEmailAddress.Items) {
sw.WriteLine(" , ," + item.ToString());
}
}
However since myCsvFileStream is not initialized (because stream is a static class) it is always null.
Here is what I do with the data (byte stream) after creating the csv.
byte[] file = new byte[nFileLen];
myCsvFileStream.Read(file, 0, nFileLen);
bool response = this.repositoryService.SaveUsers(this.SelectedAccount.Id, file, this.authenticatedUser.SessionToken.SessionId);
myCsvFileStream.Close();
In the end I used StringBuilder to create my csv file contents. Then got a byte array of its contents and used that to populate my shared stream (I say shared because when the user enters their own CSV file it is a HttpPostedFile but when sending it to our server via the rest call (respositoryservices.saveusers) it uses the same byte stream that it would via this method)
StringBuilder csvFileString = new StringBuilder();
sharedStreamForBatchImport = new MemoryStream();
foreach (ListItem item in this.lstAddEmailAddress.Items) {
csvFileString.Append(",," + item.ToString() + "\\r\\n");
}
//get byte array of the string
byteArrayToBeSent = Encoding.ASCII.GetBytes(csvFileString.ToString());
//set length for read
byteArraySize = (int)csvFileString.Length;
//read bytes into the sharedStreamForBatchImport (byte array)
sharedStreamForBatchImport.Read(byteArrayToBeSent, 0, byteArraySize);
You want to create a new MemoryStream()
Here is a function I use to write CSV files
public static bool WriteCsvFile(string path, StringBuilder stringToWrite)
{
try
{
using (StreamWriter sw = new StreamWriter(path, false)) //false in ordre to overwrite the file if it already exists
{
sw.Write(stringToWrite);
return true;
}
}
catch (Exception)
{
return false;
}
}
stringToWrite is just a string that has been created that way :
public static bool WriteCsvFile(string path, DataTable myData)
{
if (myData == null)
return false;
//Information about the table we read
int nbRows = myData.Rows.Count;
int nbCol = myData.Columns.Count;
StringBuilder stringToWrite = new StringBuilder();
//We get the headers of the table
stringToWrite.Append(myData.Columns[0].ToString());
for (int i = 1; i < nbCol; ++i)
{
stringToWrite.Append(",");
stringToWrite.Append(myData.Columns[i].ToString());
}
stringToWrite.AppendLine();
//We read the rest of the table
for (int i = 0; i < nbRows; ++i)
{
stringToWrite.Append(myData.Rows[i][0].ToString());
for (int j = 1; j < nbCol; ++j)
{
stringToWrite.Append(",");
stringToWrite.Append(myData.Rows[i][j].ToString());
}
stringToWrite.AppendLine();
}
return WriteCsvFile(path, stringToWrite);
}

Rewriting a text file after reading it

i got a file that is store in my appliction directory, and he got some site list.
i dont have any problem reading it, but when i want to write to it, i get
System.ArgumentException: Stream is not writeable
this is how i accsess the file:
FileStream theTextFileStream = new FileStream(Environment.CurrentDirectory + "/fourmlinks.txt",FileMode.OpenOrCreate);
and this is the function that throw me the expection:
public static void WriteNewTextToFile(string text, FileStream theFile)
{
string fileText = GetAllTextFromFile(theFile);
ArrayList fileLIst = populateListFromText(fileText);
using (StreamWriter fileWriter = new StreamWriter(theFile))
{
fileWriter.Write(String.Empty);
for (int i = 0; i < fileLIst.Count; i++)
{
fileWriter.WriteLine(fileLIst[i].ToString());
}
}
}
the function read the old and new text and add it to an arry. then i clean the file from every thing, and rewriting it with the old and new data from the arry i made.
i dont know if that will help but here is the file proprites:
Build Action: None
Copy To Out Put Directory: Copy always
why i cant rewrite the file?
this is the function i use to read the file content:
public static string GetAllTextFromFile(FileStream theFile)
{
string fileText = "";
using (theFile)
{
using (StreamReader stream = new StreamReader(theFile))
{
string currentLine = "";
while ((currentLine = stream.ReadLine()) != null)
{
fileText += currentLine + "\n";
}
}
}
return fileText;
}
You have to use Read/Write file access as third parameter -
FileStream theTextFileStream = new FileStream(Environment.CurrentDirectory + "/fourmlinks.txt",FileMode.OpenOrCreate, FileAccess.ReadWrite
);
Important - Remove using(theFile) statement:
public static string GetAllTextFromFile(FileStream theFile)
{
string fileText = "";
using (StreamReader stream = new StreamReader(theFile))
{
string currentLine = "";
while ((currentLine = stream.ReadLine()) != null)
{
fileText += currentLine + "\n";
}
}
return fileText;
}
Do not use using construct in your case as it will close the underlying stream as in your case you have to manually open and close stream objects.
This will allow you to write in the file as well.
For more information refer following links -
FileStream Constructor
FileAccess Enumeration

Categories