As part of another project, I found this article explaining how to bring up a SaveFileDialog.
But in the center of the code there is a comment that simply reads
//Code to write the stream goes here.
and seeing as I don't know how to do this either I am at a bit of a loss.
In the end my code will be compiling a list of user selections, each separated by a newline character, and then saving that list to a .json with a name and a location specified by the user. The user will have the option to either create a new .json or overwrite an old one.
I'm not including any code snippets since right now, without the knowledge of how to properly write to a stream, there's really nothing to show that is relevant. If you would like more details though just ask. I'll do my best to flesh out my issue.
This should do the job for you:
private void SaveString(string data)
{
var byteData = Encoding.UTF8.GetBytes(data);
var saveFileDialog = new SaveFileDialog
{
DefaultExt = "json",
AddExtension = true,
Filter = "JSON|*.json"
};
if (saveFileDialog.ShowDialog() != DialogResult.OK ||
string.IsNullOrEmpty(saveFileDialog.FileName)) return;
using (var saveFileDialogStream = saveFileDialog.OpenFile())
{
saveFileDialogStream.Write(byteData, 0, byteData.Length);
}
}
Related
I've created a zip file method in my web api which returns a zip file to the front end (Angular / typescript) that should download a zip file in the browser. The issue I have is the file shows it has data by the number of kbs it has but on trying to extract the files it says it's empty. From a bit of research this is most likely down to the file being corrupt, but I want to know where I can find this is going wrong. Here's my code:
WebApi:
I won't show the controller as it basically just takes the inputs and passes them to the method. The DownloadFileResults passed in basically have a byte[] in the File property.
public FileContentResult CreateZipFile(IEnumerable<DownloadFileResult> files)
{
using (var compressedFileStream = new MemoryStream())
{
using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update))
{
foreach (var file in files)
{
var zipEntry = zipArchive.CreateEntry(file.FileName);
using (var entryStream = zipEntry.Open())
{
entryStream.Write(file.File, 0, file.File.Length);
}
}
}
return new FileContentResult(compressedFileStream.ToArray(), "application/zip");
}
}
This appears to work in that it generates a result with data. Here's my front end code:
let fileData = this._filePaths;
this._fileStorageProxy.downloadFile(Object.entries(fileData).map(([key, val]) => val), this._pId).subscribe(result => {
let data = result.data.fileContents;
const blob = new Blob([data], {
type: 'application/zip'
});
const url = window.URL.createObjectURL(blob);
window.open(url);
});
The front end code then displays me a zip file being downloaded, which as I say appears to have data due to it's size, but I can't extract it.
Update
I tried writing the compressedFileStream to a file on my local and I can see that it creates a zip file and I can extract the files within it. This leads me to believe it's something wrong with the front end, or at least with what the front end code is receiving.
2nd Update
Ok, turns out this is specific to how we do things here. The request goes through platform, but for downloads it can only handle a BinaryTransferObject and I needed to hit a different end point. So with a tweak to no longer returning a FileContentResult and hitting the right end point and making the url simply an ahref it's now working.
I need to be able to take a text file with unknown encoding (e.g., UTF-8, UTF-16, ...) and copy it line by line, making specific changes as I go. In this example, I am changing the encoding, however there are other uses for this kind of processing.
What I can't figure out is how to determine if the last line has a newline! Some programs care about the difference between a file with these records:
Rec1<newline>
Rec2<newline>
And a file with these:
Rec1<newline>
Rec2
How can I tell the difference in my code so that I can take appropriate action?
using (StreamReader reader = new StreamReader(sourcePath))
using (StreamWriter writer = new StreamWriter(destinationPath, false, outputEncoding))
{
bool isFirstLine = true;
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (isFirstLine)
{
writer.Write(line);
isFirstLine = false;
}
else
{
writer.Write("\r\n" + line);
}
}
//if (LastLineHasNewline)
//{
// writer.Write("\n");
//}
writer.Flush();
}
The commented out code is what I want to be able to do, but I can't figure out how to set the condition lastInputLineHadNewline! Remember, I have no a priori knowledge of the input file encoding.
Remember, I have no a priori knowledge of the input file encoding.
That's the fundamental problem to solve.
If the file could be using any encoding, then there is no concept of reading "line by line" as you can't possibly tell what the line ending is.
I suggest you first address this part, and the rest will be easy. Now, without knowing the context it's hard to say whether that means you should be asking the user for the encoding, or detecting it heuristically, or something else - but I wouldn't start trying to use the data before you can fully understand it.
As often happens, the moment you go to ask for help, the answer comes to the surface. The commented out code becomes:
if (LastLineHasNewline(reader))
{
writer.Write("\n");
}
And the function looks like this:
private static bool LastLineHasNewline(StreamReader reader)
{
byte[] newlineBytes = reader.CurrentEncoding.GetBytes("\n");
int newlineByteCount = newlineBytes.Length;
reader.BaseStream.Seek(-newlineByteCount, SeekOrigin.End);
byte[] inputBytes = new byte[newlineByteCount];
reader.BaseStream.Read(inputBytes, 0, newlineByteCount);
for (int i = 0; i < newlineByteCount; i++)
{
if (newlineBytes[i] != inputBytes[i])
return false;
}
return true;
}
Alright it has come to this. I searched this website among many others and no one can seem to give me a straight answer so I'm going to try just asking outright. Been on this issue for about a solid 3 days and I can't afford to waste any more time on it.
Goal: The app I am building is in WPF and is going to be used as a bug tracker for a project my design team and I will be undertaking soon. Since we are going to be building a game in C++ most of the errors that occur will have a visual element to them so I inlcuded functionality to provide an image of the error in question when the user adds a bug to the list. I then take that image and save it to a local directory (for testing). Now the image path in the Error object points to a path that leads to the local directory. This functionality has been tested and works fine. My problem showes up when I want to delete a bug from the list. I am getting that very infamous "IO Exception" saying that the image I want to delete is being used by another process.
So Far: At first I tried very elegant solutions, but as with all things you get to a point where you just want to see if you can get the thing to even work at all. So I am at the point where most of the code I am using is experimental and radical. So please when looking at it note that the code being used is out of desperation, so any "simple" solutions have probably already been tried (I did research this a lot becuase I hate having to do this). Things i can think of off the top of my head are the obsurd amount of disposes and forced garbage collections being called so please to not comment on the negative nature of this practice, I am well aware :).
The Code
Saving image to local directory
public void OnBrowseClick()
{
Microsoft.Win32.OpenFileDialog openBox = new Microsoft.Win32.OpenFileDialog();
// Show dialog box to user and grab output
Nullable<bool> result = openBox.ShowDialog();
if (result == true)
{
// Create temp variable to hold local path string
string localPath = Directory.GetCurrentDirectory();
// Grab the extension of the specified file path
string extension = openBox.FileName.Substring(openBox.FileName.LastIndexOf("\\"));
// Add extension to local path
localPath += extension;
// Create local copy of image at given file path (being ridiculous at this point)
using (Stream stream = new FileStream(openBox.FileName, FileMode.Open, FileAccess.ReadWrite))
{
using (Bitmap bmp = LoadImage(stream))
{
using (Bitmap temp = (Bitmap)bmp.Clone())
{
temp.Save(localPath);
temp.Dispose();
}
bmp.Dispose();
}
stream.Dispose();
}
// Set the URL in the image text box (UI stuff)
LocalError.ImagePath = localPath;
}
}
The following is the LoadImage function that is used in the function above
private Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap bitmap = new Bitmap(stream))
{
retval = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);
using (Graphics gdi = Graphics.FromImage(retval))
{
gdi.DrawImageUnscaled(bitmap, 0, 0);
gdi.Flush();
gdi.Dispose();
bitmap.Dispose();
}
}
// Garbage collection here to be safe
GC.WaitForPendingFinalizers();
GC.Collect();
return retval;
}
And finally we come to where I try to delete the image
public void OnDeleteClick()
{
// Ask user to make sure they want to delete selected item(s)
MessageBoxResult result = MessageBox.Show("Are you sure you want to delete selected item(s) from the list?",
"Delete", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
for( int i = 0; i < Parent.ErrorListControl.ErrorDataGrid.SelectedItems.Count; ++i)
{
// Get path to image
string path = (Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i] as Error).ImagePath;
// Even tried calling garbage collection here!!!!!
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
File.Delete(path);
// Remove the error from the error list
Parent.ErrorListVM.ErrorList.Remove((Error)Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i]);
// Decrement counter because we altered the list while in a loop
i--;
}
}
}
Notes: If anyone would like me to explain anything further or if you need to know something I left out please just ask I will get back to you ASAP! Any suggestions are helpful at this point I have absolutley no idea what I am doing wrong. I generally only program in a C++ environment so I tend to manage my own memory this whole "garbage collection" thing is really throwing a wrench in our project! (Off topic note: I do not know why I am not getting any color highlighting so I apologize to anyone who takes the time to read this).
Here's a simple way to do what you want. In this example, I'm using Path.GetTempFileName() to generate a random file name in the local user's temp directory. If you don't need to persist the files then it's a good place to store them temporarily. Also, the user could theoretically import two files with the same name. So you want to use some kind of random filename generation or other mechanism to avoid conflicts.
private void browseButton_Click(object sender, RoutedEventArgs e)
{
var openFileDialog = new Microsoft.Win32.OpenFileDialog();
if (openFileDialog.ShowDialog(this) == true)
{
using (Bitmap originalImage = new Bitmap(openFileDialog.FileName))
{
string tempFileName = System.IO.Path.GetTempFileName();
originalImage.Save(tempFileName);
// LocalError.LocalPath
LocalPath = tempFileName;
}
}
}
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(LocalPath))
{
File.Delete(LocalPath);
}
}
Although a simple File.Copy should suffice as long as you have the right paths, I was just providing a solution that matched your question.
EDIT:
Actually the current directory does not seem to be changed by the OpenFileDialog. I could swear that it did at some point. So I don't think this is your problem. Regardless, this code still works for me and you shouldn't require anything more complicated than this.
EDIT #2:
It seems the lock is actually caused by the image being databound to the view and presumably locked by the BitmapSource. You should be able to create it without locking the file. Generally, this is slower so don't do it this way unless you need to be able to modify or delete the file.
bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.CacheOption = BitmapCacheOption.OnLoad;
bitmapSource.CreateOption = BitmapCreateOptions.IgnoreImageCache;
bitmapSource.UriSource = new Uri(ImagePath, UriKind.Absolute);
bitmapSource.EndInit();
Since your LoadImage method does simple copy of the image, why not use File.Copy(source, dest) and avoid all the bitmaps, drawings, etc? Your goal might be to modify local bitmap after it's created, but it can still be done after copy.
Also, when using the using block, explicit .Dispose() is not required, as using block does it for you:
using (var obj = new SomeDisposableObject())
{
// code here
// obj.Dispose(); <-- not needed, since...
} // ...at this point obj.Dispose is called automatically.
Is there a way to store the content of a file as string or as a dictionary instead of just its file path/name?
Below is the method that I am currently using for getting the file path from a Windows Form. Is there a way to adapt it or should I start from scratch? I am loading .ini files which is only text. LINQ seems to be one route but I am not familiar with it.
public void ShowSettingsGui()
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Multiselect = false;
ofd.Filter = "Data Sources (*.ini)|*.ini*|All Files|*.*";
if (ofd.ShowDialog() == DialogResult.OK)
{
string[] filePath = ofd.FileNames;
}
m_settings = Path.GetDirectoryName(ofd.FileName);
}
LINQ is indeed a nice way to do it: We simply convert the paths to a dictionary (where they become the keys). The values are determined by calling File.ReadAllText on every file path.
var dialog = new OpenFileDialog() { Multiselect = true,
Filter = "Data Sources (*.ini)|*.ini*|All Files|*.*" };
if (dialog.ShowDialog() != DialogResult.OK) return;
var paths = dialog.FileNames;
var fileContents = paths.ToDictionary(filePath => filePath, File.ReadAllText);
To help you understand what's going one here, take a look at the (roughly equivalent) non-LINQ version. Here, we explicitly iterate over the FileNames and add them as keys to our dictionary while again calling File.ReadAllText on every one of them.
// same as first snippet without the last line
foreach (var filePath in paths)
{
fileContents.Add(filePath, File.ReadAllText(filePath));
}
Set a breakpoint to the last line of each snippet, run them and take a look at the contents of the dictionary to determine the result.
EDIT: It wasn't clear in the question, but it seems you're only interested in a single file name. That means you don't need LINQ at all (m_settings needs to be a string).
var dialog = new OpenFileDialog{Filter = "Data Sources (*.ini)|*.ini*|All Files|*.*"};
if (dialog.ShowDialog() != DialogResult.OK) return;
m_settings = File.ReadAllText(dialog.FileName);
if you can add description of what are you trying to accomplish it would help.
just the same, I would use to store/read settings by using the settings class
here is a link to how to use it:
write user settings
I used in the past xml to parse a settings file, i find it much easier than reading ini in a sequential manner.
Hope it helps
I am working on this project still and I am running into a problem. Well here is what I need to do.
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside and handle any exceptions that arise.
Ok here is my code:
private void Save_Click(object sender, EventArgs e)
{
string filePath = txtFilePath.Text;
if (!File.Exists(filePath))
{
FileStream fs = File.Create(filePath);
fs.Close();
}
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(fs))
{
foreach (string line in employeeList.Items)
{
sw.WriteLine(line);
}
}
}
}
Now when I go onto my program and want to save something from the employeelist.text that its not being saved to the place I am saving it at. I don;t know if I am missing something in my code or what but it will not save. Here is an example:
I add a person name to this list in employeelist and in the textbox I
have a file called C:\employess\employeelist.txt I want to save it to.
I click the save button then I go to that employeelist and it is not
being saved.
I don't know what I am doing wrong I have been looking online for a solution but I haven't found anything yet. Thanks
Some things to double-check:
Make sure you don't have the employeelist.txt file open when you're testing
Make sure you don't have invalid characters in your file name
Make sure your application has permission to save the file to the location you specified
Use the debugger to step-through your code and look for swallowed exceptions -- there must be a reason the file is not created.
Check that your Save_Click event is wired up to your button -- is the code in your example even running?
Once you check those things, you may want to follow this example for the create vs. append requirement of your project:
string path = txtFilePath.Text;
// This text is added only once to the file.
if (!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (var line in employeeList.Items)
sw.WriteLine(line.ToString());
}
}
else
{
using (StreamWriter sw = File.AppendText(path))
{
foreach (var line in employeeList.Items)
sw.WriteLine(line.ToString());
}
}
This will create the file if it doesn't exist, or append to it if it does.
Checking that the file exists and then creating it is a bit unnecessary as this can all be handled by the StreamWriter/FileStream parts. So your above function can be simplified into the following:
public void Save_Click(object sender, EventArgs e)
{
StreamWriter file =
new StreamWriter(txtFilePath.Text, true);//Open and append
foreach (object item in employeeList.Items) {
file.WriteLine(item.toString());
}
file.Close();
}
[Updated]
What are the types of txtFilePath and employeeList the former suggests it's a text box, the later suggests it's bound to a non-GUI element perhaps? (WAG)
You might also want to append a blank line at the end so that on further saves you can tell it was an append rather than one long list (depending on your needs of course)
Starting with .Net Framework 4 you can do it like this:
private void Save_Click(object sender, EventArgs e)
{
File.AppendAllLines(txtFilePath.Text, employeeList.Items);
}
Of course, you probably would want to add a check to have a valid path and a valid enumeration of strings.
If the path looks like a relative one (i.e. doesn't begin with a drive letter), then it will be interpreted that way.
If you put a full path in the text box, does the file get saved in the proper place? If so, perhaps this is what was intended.
If the user doesn't put in a full path, do you have a way to make it one (for example, just sticking C:\ at the beginning)? Or at least can you tell when there isn't a full path, and reject the request?