Google Drive API Upload Command ResponseBody always null C# - c#

I am working on a system powered by a Raspberry Pi. As part of this I am attempting to upload a text file to my google drive from the Pi. As a first test, I have a UWP app to create a text file of temperature and humidity data and try to upload it.
When it gets to the upload command, nothing happens. No exceptions are thrown but neither are any files uploaded. I have searched the web and followed lots of dead ends, but without an error I don't really know where to begin to find out why the file is not uploading; I have followed the instructions from Google and like I say, nothing throws an exception, it just doesn't work... Any suggestions would be really welcome as I have been spending a long time Googling for answers now and found nothing.
here is the meat of the code behind the xaml page of the uwp app (I can send you the whole project if you ask). I have not included a couple of the methods in the code below as it is only the method StoreValues() that is giving me gyp it the moment. This is the first iteration of the project that has not thrown an error.
public sealed partial class MainPage : Page
{
static string[] Scopes = { DriveService.Scope.Drive };
static string AppName = "Drive API .NET Quickstart";
static string fileName = "Environmental_Data";
static string fileExtension = ".txt";
private const int DHTPIN = 4;
private IDht dht = null;
private GpioPin dhtPin = null;
private DispatcherTimer sensorTimer = new DispatcherTimer();
private int counterWrite = 0;
private int counterStore = 0;
//the number on the end of the filename that will be incremented each time a file is produced.
//TODO remove old files and reset counter periodically
private int fileId = 1;
//read values every this many seconds
private int readInterval = 1;
//write a new line to the file every this many readings
private int writeCollectionSize = 2;
//send file to cloud every this many lines
private int storeCollectionSize = 4;
private StorageFolder Folder = ApplicationData.Current.LocalFolder;
public List<Environment> Readings;
public StorageFile File1;
public MainPage()
{
InitializeComponent();
InitializeLocalFiles(fileId);
InitializePi();
}
private async void InitializeLocalFiles(int fileId)
{
try
{
//make a new target file to save our data to
var newFile = $"{fileName}_{fileId.ToString()}{fileExtension}";
File1 = await Folder.CreateFileAsync(newFile, CreationCollisionOption.ReplaceExisting);
File1 = await Folder.GetFileAsync(newFile);
//let a human know where the first file is
Debug.WriteLine(File1.Path);
}
catch (Exception ex)
{
Debug.WriteLine("error incountered in InitializeLocalFiles: {0}", ex.Message);
}
}
private void InitializePi()
{
try
{
//set up the pins etc
dhtPin = GpioController.GetDefault().OpenPin(DHTPIN, GpioSharingMode.Exclusive);
dht = new Dht11(dhtPin, GpioPinDriveMode.Input);
//This is what the environment class looks like: public class Environment
//{ public decimal Temperature; public decimal Humidity; public DateTime DateTime; }
Readings = new List<Environment>();
sensorTimer.Interval = TimeSpan.FromSeconds(readInterval);
//add the tick to the clock and set it going
sensorTimer.Tick += sensorTimer_Tick;
sensorTimer.Start();
//initialise the app
temperatureMeter.Value = "OFF";
humidityMeter.Value = "OFF";
}
catch (Exception ex)
{
Debug.WriteLine("error incountered in InitialisePi: {0}", ex.Message);
}
}
private void sensorTimer_Tick(object sender, object e)
{
try
{
//every tick of the clock, we will read the sensor to get new values
readSensor();
//if we got to the end of the write collection, then it is time to aggregate the figures and write a line to the local file
if (counterWrite == writeCollectionSize)
{
counterWrite = 0;
Readings.AggregateListToCSV(File1);
//clear the list of readings ready for the next tick
Readings.Clear();
}
//if we got to the end of the store collection, it's time to send the local file on a trip around the world
if (counterStore == storeCollectionSize)
{
counterStore = 0;
StoreValues(File1);
//rather than deleting the file, we will make a new one for the next go-round
fileId += 1;
InitializeLocalFiles(fileId);
}
}
catch (Exception ex)
{
Debug.WriteLine("error incountered in sensorTimer_Tick: {0}", ex.Message);
}
}
private async void StoreValues(StorageFile file)
{
try
{
UserCredential credential;
string cred = #"{""installed"":{""client_id"":""* **REDACTED FOR FORUM QUESTION ***"",""redirect_uris"":[""urn:ietf:wg:oauth:2.0:oob"",""http://localhost""]}}";
byte[] byteArray = Encoding.UTF8.GetBytes(cred);
//I'm using credentials stored in memory, but you could use a json file
using (var stream =
//new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
new MemoryStream(byteArray))
{
//TODO find out why I have to copy the token.json file every time to the app folder - for some reason it does not copy over even though set to Copy Always
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
}
//set up the stuff that the Google Drive seems to require. I don't pretend to understand this bit
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = AppName,
});
service.HttpClient.Timeout = TimeSpan.FromMinutes(100);
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Name = "My Report",
MimeType = "application/vnd.google-apps.file"
};
FilesResource.CreateMediaUpload request;
//using (var stream = new FileStream(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var stream = File.Open(file.Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
request = service.Files.Create(fileMetadata, stream, "text/plain");
request.Fields = "id";
await request.UploadAsync();
}
//request.ResponsBody is always NULL - why??
var responseFile = request.ResponseBody;
//this line causes an error "attempted to read or write protected memory"
if (responseFile == null)
{
Debug.WriteLine($"** ERROR ** File {fileName}_{(fileId - 1).ToString()}{fileExtension} could not be uploaded.");
}
else
{
Debug.WriteLine($"File ID: {responseFile.Id.ToString()}");
}
}
catch(Exception ex)
{
Debug.WriteLine("error incountered in StoreValues: {0}", ex.Message);
}
}
}
this yields a Debug Output that looks like this (a snippet shown)
....
C:\Data\Users\DefaultAccount\AppData\Local\Packages\e50d180b-3aea-4119-a4ff-72592c8ac7b5_4j4qdv46jwqvm\LocalState\Environmental_Data_6.txt
Lines ready to write: 1 | Lines ready to store: 1
** ERROR ** File Environmental_Data_5.txt could not be uploaded.
Lines ready to write: 2 | Lines ready to store: 2
18.9,10.0,2/16/2019 7:32:16 AM
Lines ready to write: 1 | Lines ready to store: 3
Lines ready to write: 2 | Lines ready to store: 4
18.9,9.9,2/16/2019 7:32:19 AM
C:\Data\Users\DefaultAccount\AppData\Local\Packages\e50d180b-3aea-4119-a4ff-72592c8ac7b5_4j4qdv46jwqvm\LocalState\Environmental_Data_7.txt
** ERROR ** File Environmental_Data_6.txt could not be uploaded.
Lines ready to write: 1 | Lines ready to store: 1
Lines ready to write: 2 | Lines ready to store: 2
18.9,10.1,2/16/2019 7:32:22 AM
Lines ready to write: 1 | Lines ready to store: 3
Lines ready to write: 2 | Lines ready to store: 4
The thread 0xfac has exited with code 0 (0x0).
Lines ready to write: 1 | Lines ready to store: 1
18.9,10.05,2/16/2019 7:32:25 AM
C:\Data\Users\DefaultAccount\AppData\Local\Packages\e50d180b-3aea-4119-a4ff-72592c8ac7b5_4j4qdv46jwqvm\LocalState\Environmental_Data_8.txt
** ERROR ** File Environmental_Data_7.txt could not be uploaded.
Lines ready to write: 2 | Lines ready to store: 2
...
I believe I have all the credential etc in place - I have to copy the token.jsn file that was generated from my credentials onto the pi every time I deploy, but nonetheless it all seems to be connecting as it should.
While I'm beginning to get a bit of an understanding of the beginnings of wpf, this is all very much outside my ken and after 3 weeks of trying to sort this problem on and off, I'm really at the end of my tether - any suggestions would be brilliant.

Related

DeleteMessageAsync() not deleting message in SQS queue .Net Core

I am trying to delete message in SQS queue, but it is not deleting in the queue. I have been trying to make a lot of changes, but is still not working. I am new to c#, .net core, and AWS. Can anyone please help me with this?
Here is my main method:
[HttpGet]
public async Task<ReceiveMessageResponse> Get()
{
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest
{
WaitTimeSeconds = 3 //it'll ping the queue for 3 seconds if I don't do this, sometimes I receive message and sometimes I don't
};
receiveMessageRequest.QueueUrl = myQueueUrl;
receiveMessageRequest.MaxNumberOfMessages = 10; // can change number of messages as needed
//receiveing messages/responses
var receiveMessageResponse = await amazonSQSClient.ReceiveMessageAsync(receiveMessageRequest);
if (receiveMessageResponse.Messages.Count > 0){
var bucketName = getBucketName(receiveMessageResponse);
var objectKey = getObjectKey(receiveMessageResponse);
var versionId = getVersionId(receiveMessageResponse);
string filePath = "C:\\InputPdfFile\\"; // change it later
string path = filePath + objectKey;
//get the file from s3 bucket and download it in in
var downloadInputFile = await DownloadAsync(path, versionId, objectKey);
//Get score from the output file
string jsonOutputFileName = "\\file-1.txt"; //change it later from text file to json file
string jsonOutputPath = "C:\\OutputJsonFile"; //change it later
string jasonArchivePath = "C:\\ArchiveJsonFile"; //change it later
int score = GetOutputScore(jsonOutputPath, jsonOutputFileName);
//update metadata from the score received from ML worker (GetOutputScore)
PutObjectResponse putObjectResponse = await UpdateMetadataAsync(score);
//Move file from output to archive after updating metadata
string sourceFile = jsonOutputPath + jsonOutputFileName;
string destFile = jasonArchivePath + jsonOutputFileName;
if (!Directory.Exists(jasonArchivePath))
{
Directory.CreateDirectory(jasonArchivePath);
}
System.IO.File.Move(sourceFile, destFile);
//delete message after moving file from archive
*DeleteMessage(receiveMessageResponse);* //not sure why it is not deleting**
}
return receiveMessageResponse;
}
Here is my Delete method:
public async void DeleteMessage(ReceiveMessageResponse receiveMessageResponse)
{
if (receiveMessageResponse.Messages.Count > 0)
{
foreach (var message in receiveMessageResponse.Messages)
{
var delRequest = new DeleteMessageRequest
{
QueueUrl = myQueueUrl,
ReceiptHandle = message.ReceiptHandle
};
var deleteMessage = await amazonSQSClient.DeleteMessageAsync(delRequest);
}
}
else // It is not going in else because the message was found but still not deleting it
{
Console.WriteLine("No message found");
}
}
Any help would be greatly appreciated!

How to be protected against power outages when writing app state to disk

I have an application state which should be stored to a file. Each time it makes a new file with different name (like "data.bin.new") and then after writing deletes the original "data.bin" and renames "data.bin.new". Recently I had a power surgery and the data file was corrupted. How it could happened and how to protect against it?
public void SaveData()
{
string newName = _dataFile + ".new";
string bckName = _dataFile + ".bck";
string normalName = _dataFile;
using (var f = File.Create(newName))
{
f.SetLength(0);
AqlaSerializer.Serializer.Serialize(f, V);
}
var test = LoadData(newName);
if (!typeof(T).IsValueType && ReferenceEquals(test, null)) throw new Exception("Can't load what's been saved");
if (File.Exists(bckName)) File.Delete(bckName);
if (File.Exists(normalName)) File.Move(normalName, bckName);
File.Move(newName, normalName);
}

UWP app crashes (but not all the time) during function execution

I'm working on a proof of concept at the moment, just for fun (and for YouTube). The thing I am trying to prove is that I can efficiently "hack" WiFi passwords using UWP and C# for Windows. I don't know of any Wi-Fi cracking tools that are designed specifically for Windows 10 devices (PC, Tablet, XboxOne, Mobile etc)...
So I have actually managed to perform a dictionary style attack (on my own WiFi network of course). However my function seems to completely crash occasionally when running the "hack".
Please consider the fact that this is completely white hat hacking I am talking about here, nothing illegal is intended.
Any help with a reason why this crashes is appreciated...
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
int success = 0;
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
do
{
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
}
} while (success == 0);
}
}
This is the code that actually runs a dictionary-style "hack" on a selected network. The code to actually connect to the network is as follows:
private async Task<bool> checkWifiPassword(string passPhrase)
{
var credential = new PasswordCredential();
WiFiReconnectionKind reconnectionKind = WiFiReconnectionKind.Manual;
credential.Password = passPhrase;
var selectedNetwork = null as WiFiNetworkDisplay;
foreach (var network in ResultCollection)
{
if (WifiNetworks.SelectedItem.ToString() == network.Ssid)
{
selectedNetwork = network as WiFiNetworkDisplay;
}
}
if (selectedNetwork != null)
{
var result = await firstAdapter.ConnectAsync(selectedNetwork.AvailableNetwork, reconnectionKind, credential);
if (result.ConnectionStatus == WiFiConnectionStatus.Success)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
Does anyone have any idea what I am missing here?
Any help appreciated.
Thanks
Consider this loop
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
This can lead to and infinite loop:
Imagine the following dictionary file:
abc
bcd
cde
where abc is the correct password.
You peek the stream, you get 97 (decimal ASCII for letter a), fine.
Success is 0, as we just started.
You read the next line.
You check the password, it works, cool.
You set success to 1, show the message, etc.
User closes the message dialog, ShowAsync() returns.
End of first loop iteration, let's start another one.
You peek the stream, you get 98 (ASCII for letter b), non 0, fine.
Success is not zero, so we skip the entire body of that while, end of second loop iteration.
You peek the stream again, the pointer did not move since the last peek, so you're going to get that same 98 again.
And you skip again, infinite loop.
EDIT - there is actually another infinite loop there
I will not detail this that much, but take a look at the outer do-while loop. That loop runs until success. But if the inner loop exhausts all possibilities and does not find a correct password, success will remain 0. That means the do while will run once again, the inner loop will go through the file again, which obviously will not find a solution again, and so on.
Solution
There are many ways that code could be cleaned up, but the quick fix is to break after msg.ShowAsync();.
More details (that would belong to codereview.stackexchange.com):
Also I would not Peek the StreamReader. Use EndOfStream for that job. And you can skip the inner if, you simply break if you found a correct password. You can drop the outer loop as well. If you completed the inner loop without setting the success flag (which in turn should be a boolean) you can report to the user that no password worked.
I would do something along the lines of: (take it as a pseudo code, might not compile as it is)
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
bool success = false;
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (!streamReader.EndOfStream)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = true;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
break;
}
else
{
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
if(!success){ /* report to the user*/ }
}

C# Pdf to Text with image placeholder

I have a batch of PDFs that I want to convert to Text. It's easy to get text with something like this from iTextSharp:
PdfTextExtractor.GetTextFromPage(reader, pageNumber);
It's easy to get Images using this answer (or similar answers in the thread).
What I can't figure out easily... is how to interleave image placeholders in the text.
Given a PDF, a page # and GetTextFromPage I expect the output to be:
line 1
line 2
line 3
When I'd like it to be (Where 1.1 means page 1, image 1... Page 1, image 2):
line 1
[1.1]
line 2
[1.2]
line 3
Is there a way to get an "image placeholder" for iTextSharp, PdfSharp or anything similar? I'd like a GetTextAndPlaceHoldersFromPage method (or similar).
PS: Hrm... it's not letting me tag iTextSHARP - not iText. C# not Java.
C# Pdf to Text with image placeholder
https://stackoverflow.com/a/28087521/
https://stackoverflow.com/a/33697745/
Although this doesn't have the exact layout mentioned in my question (Since that was a simplified version of what I really wanted anyways), it does have the starting parts as listed by the second note (translated from iText Java)... with extra information pulled from the third note (Some of the reflection used in Java didn't seem to work in C#, so that info came from #3).
Working from this, I'm able to get a List of Strings representing lines in the PDF (all pages, instead of just page 1)... with text added where images should be (Huzzah!). ByteArrayToFile extension method added for flavor (Although I didn't include other parts/extensions that may break a copy/paste usages of this code).
I've also been able to greatly simplify other parts of my process and gut half of the garbage I had working before. Huzzah!!! Thanks #Mkl
internal class Program
{
public static void Main(string[] args)
{
var dir = Settings.TestDirectory;
var file = Settings.TestFile;
Log.Info($"File to Process: {file.FullName}");
using (var reader = new PdfReader(file.FullName))
{
var parser = new PdfReaderContentParser(reader);
var listener = new SimpleMixedExtractionStrategy(file, dir);
parser.ProcessContent(1, listener);
var x = listener.GetResultantText().Split('\n');
}
}
}
public class SimpleMixedExtractionStrategy : LocationTextExtractionStrategy
{
public static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public DirectoryInfo OutputPath { get; }
public FileInfo OutputFile { get; }
private static readonly LineSegment UNIT_LINE = new LineSegment(new Vector(0, 0, 1), new Vector(1, 0, 1));
private int _counter;
public SimpleMixedExtractionStrategy(FileInfo outputFile, DirectoryInfo outputPath)
{
OutputPath = outputPath;
OutputFile = outputFile;
}
public override void RenderImage(ImageRenderInfo renderInfo)
{
try
{
var image = renderInfo.GetImage();
if (image == null) return;
var number = _counter++;
var imageFile = new FileInfo($"{OutputFile.FullName}-{number}.{image.GetFileType()}");
imageFile.ByteArrayToFile(image.GetImageAsBytes());
var segment = UNIT_LINE.TransformBy(renderInfo.GetImageCTM());
var location = new TextChunk("[" + imageFile + "]", segment.GetStartPoint(), segment.GetEndPoint(), 0f);
var locationalResultField = typeof(LocationTextExtractionStrategy).GetField("locationalResult", BindingFlags.NonPublic | BindingFlags.Instance);
var LocationalResults = (List<TextChunk>)locationalResultField.GetValue(this);
LocationalResults.Add(location);
}
catch (Exception ex)
{
Log.Debug($"{ex.Message}");
Log.Verbose($"{ex.StackTrace}");
}
}
}
public static class ByteArrayExtensions
{
public static bool ByteArrayToFile(this FileInfo fileName, byte[] byteArray)
{
try
{
// Open file for reading
var fileStream = new FileStream(fileName.FullName, FileMode.Create, FileAccess.Write);
// Writes a block of bytes to this stream using data from a byte array.
fileStream.Write(byteArray, 0, byteArray.Length);
// close file stream
fileStream.Close();
return true;
}
catch (Exception exception)
{
// Error
Log.Error($"Exception caught in process: {exception.Message}", exception);
}
// error occured, return false
return false;
}
}

How to avoid c# File.ReadLines First() locking file

I do not want to read the whole file at any point, I know there are answers on that question, I want t
o read the First or Last line.
I know that my code locks the file that it's reading for two reasons 1) The application that writes to the file crashes intermittently when I run my little app with this code but it never crashes when I am not running this code! 2) There are a few articles that will tell you that File.ReadLines locks the file.
There are some similar questions but that answer seems to involve reading the whole file which is slow for large files and therefore not what I want to do. My requirement to only read the last line most of the time is also unique from what I have read about.
I nead to know how to read the first line (Header row) and the last line (latest row). I do not want to read all lines at any point in my code because this file can become huge and reading the entire file will become slow.
I know that
line = File.ReadLines(fullFilename).First().Replace("\"", "");
... is the same as ...
FileStream fs = new FileStream(#fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
My question is, how can I repeatedly read the first and last lines of a file which may be being written to by another application without locking it in any way. I have no control over the application that is writting to the file. It is a data log which can be appended to at any time. The reason I am listening in this way is that this log can be appended to for days on end. I want to see the latest data in this log in my own c# programme without waiting for the log to finish being written to.
My code to call the reading / listening function ...
//Start Listening to the "data log"
private void btnDeconstructCSVFile_Click(object sender, EventArgs e)
{
MySandbox.CopyCSVDataFromLogFile copyCSVDataFromLogFile = new MySandbox.CopyCSVDataFromLogFile();
copyCSVDataFromLogFile.checkForLogData();
}
My class which does the listening. For now it simply adds the data to 2 generics lists ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySandbox.Classes;
using System.IO;
namespace MySandbox
{
public class CopyCSVDataFromLogFile
{
static private List<LogRowData> listMSDataRows = new List<LogRowData>();
static String fullFilename = string.Empty;
static LogRowData previousLineLogRowList = new LogRowData();
static LogRowData logRowList = new LogRowData();
static LogRowData logHeaderRowList = new LogRowData();
static Boolean checking = false;
public void checkForLogData()
{
//Initialise
string[] logHeaderArray = new string[] { };
string[] badDataRowsArray = new string[] { };
//Get the latest full filename (file with new data)
//Assumption: only 1 file is written to at a time in this directory.
String directory = "C:\\TestDir\\";
string pattern = "*.csv";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
fullFilename = directory + file.ToString(); //This is the full filepath and name of the latest file in the directory!
if (logHeaderArray.Length == 0)
{
//Populate the Header Row
logHeaderRowList = getRow(fullFilename, true);
}
LogRowData tempLogRowList = new LogRowData();
if (!checking)
{
//Read the latest data in an asynchronous loop
callDataProcess();
}
}
private async void callDataProcess()
{
checking = true; //Begin checking
await checkForNewDataAndSaveIfFound();
}
private static Task checkForNewDataAndSaveIfFound()
{
return Task.Run(() => //Call the async "Task"
{
while (checking) //Loop (asynchronously)
{
LogRowData tempLogRowList = new LogRowData();
if (logHeaderRowList.ValueList.Count == 0)
{
//Populate the Header row
logHeaderRowList = getRow(fullFilename, true);
}
else
{
//Populate Data row
tempLogRowList = getRow(fullFilename, false);
if ((!Enumerable.SequenceEqual(tempLogRowList.ValueList, previousLineLogRowList.ValueList)) &&
(!Enumerable.SequenceEqual(tempLogRowList.ValueList, logHeaderRowList.ValueList)))
{
logRowList = getRow(fullFilename, false);
listMSDataRows.Add(logRowList);
previousLineLogRowList = logRowList;
}
}
//System.Threading.Thread.Sleep(10); //Wait for next row.
}
});
}
private static LogRowData getRow(string fullFilename, bool isHeader)
{
string line;
string[] logDataArray = new string[] { };
LogRowData logRowListResult = new LogRowData();
try
{
if (isHeader)
{
//Asign first (header) row data.
//Works but seems to block writting to the file!!!!!!!!!!!!!!!!!!!!!!!!!!!
line = File.ReadLines(fullFilename).First().Replace("\"", "");
}
else
{
//Assign data as last row (default behaviour).
line = File.ReadLines(fullFilename).Last().Replace("\"", "");
}
logDataArray = line.Split(',');
//Copy Array to Generics List and remove last value if it's empty.
for (int i = 0; i < logDataArray.Length; i++)
{
if (i < logDataArray.Length)
{
if (i < logDataArray.Length - 1)
{
//Value is not at the end, from observation, these always have a value (even if it's zero) and so we'll store the value.
logRowListResult.ValueList.Add(logDataArray[i]);
}
else
{
//This is the last value
if (logDataArray[i].Replace("\"", "").Trim().Length > 0)
{
//In this case, the last value is not empty, store it as normal.
logRowListResult.ValueList.Add(logDataArray[i]);
}
else { /*The last value is empty, e.g. "123,456,"; the final comma denotes another field but this field is empty so we will ignore it now. */ }
}
}
}
}
catch (Exception ex)
{
if (ex.Message == "Sequence contains no elements")
{ /*Empty file, no problem. The code will safely loop and then will pick up the header when it appears.*/ }
else
{
//TODO: catch this error properly
Int32 problemID = 10; //Unknown ERROR.
}
}
return logRowListResult;
}
}
}
I found the answer in a combination of other questions. One answer explaining how to read from the end of a file, which I adapted so that it would read only 1 line from the end of the file. And another explaining how to read the entire file without locking it (I did not want to read the entire file but the not locking part was useful). So now you can read the last line of the file (if it contains end of line characters) without locking it. For other end of line delimeters, just replace my 10 and 13 with your end of line character bytes...
Add the method below to public class CopyCSVDataFromLogFile
private static string Reverse(string str)
{
char[] arr = new char[str.Length];
for (int i = 0; i < str.Length; i++)
arr[i] = str[str.Length - 1 - i];
return new string(arr);
}
and replace this line ...
line = File.ReadLines(fullFilename).Last().Replace("\"", "");
with this code block ...
Int32 endOfLineCharacterCount = 0;
Int32 previousCharByte = 0;
Int32 currentCharByte = 0;
//Read the file, from the end, for 1 line, allowing other programmes to access it for read and write!
using (FileStream reader = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan))
{
int i = 0;
StringBuilder lineBuffer = new StringBuilder();
int byteRead;
while ((-i < reader.Length) /*Belt and braces: if there were no end of line characters, reading beyond the file would give a catastrophic error here (to be avoided thus).*/
&& (endOfLineCharacterCount < 2)/*Exit Condition*/)
{
reader.Seek(--i, SeekOrigin.End);
byteRead = reader.ReadByte();
currentCharByte = byteRead;
//Exit condition: the first 2 characters we read (reading backwards remember) were end of line ().
//So when we read the second end of line, we have read 1 whole line (the last line in the file)
//and we must exit now.
if (currentCharByte == 13 && previousCharByte == 10)
{
endOfLineCharacterCount++;
}
if (byteRead == 10 && lineBuffer.Length > 0)
{
line += Reverse(lineBuffer.ToString());
lineBuffer.Remove(0, lineBuffer.Length);
}
lineBuffer.Append((char)byteRead);
previousCharByte = byteRead;
}
reader.Close();
}

Categories

Resources