In the .NET API for Windows Store Apps the Path class does not have the GetFullPath method. I need to normalize a path though, which was easy using GetFullPath. Does anyone know another method or external code that normalizes a path? What I mean with that is for example:
add the application's path if the path does not start with a drive
handle ..\ and .\ correctly
GetFullPath is pretty complex and mimicking the functionality is not easy.
As far as i understand, in WinRT you rather work with the package's installed location or with "known" folders:
Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(fileName)
KnownFolders.DocumentsLibrary.GetFileAsync(fileName)
I did not find any replacement for GetFullPath, but developed a method that handles parent directory tokens (..\) like GetFullPath.
Here it is:
public static string NormalizePath(string path)
{
if (String.IsNullOrEmpty(path) == false)
{
// Correct multiple backslashes
path = Regex.Replace(path, #"\\+", #"\");
// Correct parent directory tokens with too many points in it
path = Regex.Replace(path, #"\\\.\.+", #"\..");
// Split the path into tokens
List<string> resultingPathTokens = new List<string>();
var pathTokens = path.Split('\\');
// Iterate through the tokens to process parent directory tokens
foreach (var pathToken in pathTokens)
{
if (pathToken == "..")
{
if (resultingPathTokens.Count > 1)
{
resultingPathTokens.RemoveAt(resultingPathTokens.Count - 1);
}
}
else
{
resultingPathTokens.Add(pathToken);
}
}
// Get the resulting path
string resultingPath = String.Join("\\", resultingPathTokens);
// Check if the path contains only two chars with a colon as the second
if (resultingPath.Length == 2 && resultingPath[1] == ':')
{
// Add a backslash in this case
resultingPath += "\\";
}
return resultingPath;
}
return path;
}
Related
I have a small GUI that I use to load/save json configuration files, the most important parameters are in the gui below:
![conf]
The problem I have been trying to solve is that I am not able to create an object inside a QTextEdit and am not sure why despite I am following official documentation on how to do that.
Below a snippet of code both for the load and save button.
Also for the sake of brevity I only kept how I did the spinbox and, of course, the textedit:
void SettingsForm::on_loadBtn_clicked()
{
// Opening file dialog....
if(listDocksConfig.isEmpty())
{
QMessageBox::information(this, tr("Message"), tr("Please Open Configuration"));
}
else
{
QJsonDocument doc;
QJsonObject obj;
QByteArray data_json;
QFile input(listDocksConfig);
if(input.open(QIODevice::ReadOnly | QIODevice::Text))
{
data_json = input.readAll();
doc = doc.fromJson(data_json);
obj = doc.object();
const double xposValue = obj["X Pos"].toDouble();
QTextEdit textEdit = QTextEdit::setText(obj["comments"]); // <- Error Here
ui->doubleSpinBox_XPos->setValue(xposValue);
ui->textEdit->setText(textEdit); // <- Error Here
}
else
{
// do something
}
}
}
void SettingsForm::on_saveBtn_clicked()
{
// saving configuration with file dialog....
if(listDocksConfig.isEmpty())
{
// do something...
}
else
{
const double xposValue = ui->doubleSpinBox_XPos->value();
QTextEdit textEdit = ui->textEdit->setPlainText(); // <- Error Here
QJsonDocument doc;
QJsonObject obj;
obj["X Pos"] = xposValue;
obj["comments"] = textEdit.toString(); // <- Error Here
doc.setObject(obj);
QByteArray data_json = doc.toJson();
QFile output(listDocksConfig);
}
}
What I have done so far:
I consulted the official documentation on how to solve this problem, but could not figure out why that was not working. I also went ahead and try to use an alternative such as setText but still no luck.
I came across this source which I used as guidance for my example and solved almost all problems but the QTextEdit one.
This additional post was useful but still couldn't solve the problem.
Thanks for pointing to the right direction for solving this problem.
this line is wrong!!
QTextEdit textEdit = ui->textEdit->setPlainText();
setPlainText() needs const QString &text as parameter
you cant do that, read the official doc here
the method is void, ie. it returns nothing so you can not use void to init a QTextEdit object
update:
you already have a textEdit in the layout, so no reason to redefine one...
you can do:
ui->textEdit->setPlainText(obj["comments"].toString());
I'm having trouble accessing a text file that is packaged with my Windows Phone 8 app.
Here is the code:
var ResrouceStream = Application.GetResourceStream(new Uri("Data-Test.docx", UriKind.Relative));
if (ResrouceStream != null)
{
Stream myFileStream = ResrouceStream.Stream;
if (myFileStream.CanRead)
{
// logiic here
retrun "Hi";
}
}
else
{
return "hello";
}
Seems simple but the app always returns "hello". i have placed the file in root and also in assets, changed it to content - copy and do not copy, resource copy and do not copy but always it returns "hello".
Spent several hours on this and all solutions I can find show the solution or very similar above!
What am I doing wrong?
EDIT: Returns "hello" when I deploy to phone or emulator.
also tried "/Data-Test...", #"\Data-Text..., #/"Data-Test...!
UPDATE 1:
string aReturn = "";
var asm = Assembly.GetExecutingAssembly();
//Use this to verify the namespacing of the "Embedded Resource".
//asm.GetManifestResourceNames()
// .ToList()
// .ForEach(name => Debug.WriteLine(name));
var ResourceStream = asm.GetManifestResourceStream("ContosoSocial.Assets.QuizQuestions.QuizQuestions-Test1.docx");
if (ResourceStream != null) // <--CHECKED AND DOES NOT EQUAL NULL
{
Stream myFileStream = ResourceStream;
if (myFileStream.CanRead) // <-- CHEACKED AND CAN READ
{
StreamReader myStreamReader = new StreamReader(myFileStream);
LOGIC & EXCEPTION HERE...?
string myLine = myStreamReader.ReadLine();
}
else
{
aReturn = "myFileStream.CanRead = true";
}
}
else
{
aReturn = "stream equals null";
}
Debug.WriteLine(aReturn);
}
The assignment of myFileStream to a StreamReader object is throwing the exception null pointer. I thought I would wrap myFileStream to a StreamReader so I can read a line at a time..? This is my first c# project and I'm unfamiliar with it's syntax and classes.
UPDATE 2:
OK I added...
Debug.WriteLine(aReturn);
...following...
string myLine = myStreamReader.ReadLine();
...and noticed it was retrieving only the 2 characters 'PK' !
So saved the .docx file as .txt and reinserted adn changed build copy to embedded - do not copy...Happy days it now pulls off the first line in the file.
Thanks to OmegaMan for your help with this one :-)
Change file type in the project to Embedded Resource
Extract the resource by working the namespace to its location. Here is an example code where I pull in an XSD:
Code:
var asm = Assembly.GetExecutingAssembly();
// Use this to verify the namespacing of the "Embedded Resource".
// asm.GetManifestResourceNames()
// .ToList()
// .ForEach(name => Debug.WriteLine(name));
var f1 = asm.GetManifestResourceStream("UnitTests.Resources.NexusResponse.xsd");
Note this is not tested on WP8, but GetExecutingAssembly is stated to work within .Net. If you get the namespace wrong, uncomment out the code and display or debug to determine the resources and their namespace.
dear all
I'm trying to find a list of documents by "full path". And after reading the API reference, it seems to be a complex task. Assume my path is something like /path0/path1/path2/...
List children of root folder and find all children with name equals "path0" and put them to a list "result0"
Find all children of items in "result0" with name equals "path1" and put them to a list "result1"
Find all children of items in "result1" with name equals "path2" and ...
Above approach seems very low efficient cause it needs multiple interactions between my application and Drive. I understand Google Drive allows multiple files share the same file name even in the same folder. It will be handy if I can do something like:
listDocByFullPath("path0/path1/path2")
Is this possible with current version of Google Drive SDK? If it's not there yet, I was wondering if there is a simpler way than what I listed here.
BTW, as my application is purely a back-end service, it's not possible to use file picker provided by Google.
Cheers.
Unlike conventional file systems, a file could be under multiple folders on Drive. Folders are pretty much similar what labels are. Therefore, conventional paths dont always work within our abstraction. I'd suggest you to follow the logic below:
List files with q = 'root' in parents and title = 'path0' and mimeType = 'application/vnd.google-apps.folder' and pick the first result.
If there is a matching result, get the folder's id and perform another listing with '<id of path0>' in parents and title = 'path1' and mimeType='application/vnd.google-apps.folder' and pick the first result.
Keep going until you reach to your target folder.
The biggest problem is that a path does not uniquely identify the file or folder! For example, in the web UI, you can make 2 folders with the same name as children of the same folder.
i.e. you can make a tree that looks like:
root
|-somefolder
|-somefolder
Search / list with the param q set to name= and include fields param with "files(mimeType,id,name,parents)"
If there is only one search result, return this file
Else if there are multiple files, get the ID in parent and use file's get API with that ID and check if the name matches the last fragment in the path. If only one of the parent Ids match select that option else pick the matching parents and get to check the next parent element in the path
Essentially check bottom up
#Barcu Dogan is correct,, that's the only way to find full path, here is the implementation:
//BeanConfig.java
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Drive drive() throws GeneralSecurityException, IOException {
Drive drive = new Drive.Builder(GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, getCredentials())
.setApplicationName(APPLICATION_NAME)
.build();
return drive;
}
private static HttpRequestInitializer getCredentials() throws IOException {
// Load client credentials from path
GoogleCredentials credential = GoogleCredentials.fromStream(new FileInputStream(CREDENTIALS_FILE_PATH))
.createScoped(DriveScopes.all());
return new HttpCredentialsAdapter(credential);
}
//FileHelper.java
//pass folderId or fileId
public String getCompletePath(Drive drive, String folderId) {
String path = "";
try {
File files = drive.files()
.get(folderId).setFields("id,name,parents")
.execute();
return recursivePath(drive, files, path);
} catch (IOException e) {
e.printStackTrace();
}
return path;
}
public String recursivePath(Drive drive, File currentFolder, String path) throws IOException {
if (currentFolder == null || currentFolder.getParents() == null || currentFolder.getParents().isEmpty())
return path;
if (!path.equalsIgnoreCase("")) {
path = currentFolder.getName() + "/" + path;
} else {
path = currentFolder.getName();
}
File parentFolder = drive.files().get(currentFolder.getParents().get(0)).setFields("id,name,parents").execute();
return recursivePath(drive, parentFolder, path);
}
Is there any way to get all files and folder in box without knowing their id? Also, how to get all collaboration objects if I don't know collaboration id?
You can get root folders and files by specifying folder id = 0.
And with this result further folders or files can also be fetched.
You can use Get Folder Items on a folder id you do know to retrieve the IDs of the folders and files that it contains. As Shanky says, use 0 to start at the root folder.
Get Collaborations will show the collaborations on a folder. You don't need any information about the collaboration, just the folder id.
Unlike systems that are built exclusively on path-access, Box gives you a durable ID for each folder and file. This has a bunch of advantages. One of the big ones is that you can rename, or move a file around, and getting to it never needs to change. It also means that you can persist the IDs, associate them with some other entity in your own system, and still be able to get back to the same file or folder. Assuming of course that you are still allowed access to it. Permissions can change too of course.
You can get all the collaborations for a user by calling GET /collaborations or all the collaborations on a folder by calling GET /folder/ID/collaborations
Call listFilesInBoxFolders("0") ---> This will parse all files and folders starting from root
public void listFilesInBoxFolders(String folderId) {
try {
// get a list of songs
BoxApiFolder folderApi = new BoxApiFolder(mSession);
BoxListItems items = folderApi.getItemsRequest(folderId).send();
JSONObject object = new JSONObject(items.toJson());
LogUtils.log(TAG, "Object " + object.toString());
JSONArray array = object.getJSONArray("entries");
for (int i = 0; i < array.length(); i++) {
JSONObject object1 = array.getJSONObject(i);
String type = object1.getString("type");
String id = object1.getString("id");
String name = object1.getString("name");
if (type.equals("folder")) {
listFilesInBoxFolders(id);
} else if (type.equals("file")) {
// Supported Media file types
if (name.contains("mp3") || name.contains("m4a") || name.contains("flac")) {
musicItems.add(new BoxMusicItem(id, name));
}
}
}
LogUtils.log(TAG, "array " + array.toString());
} catch (BoxException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
LogUtils.log(TAG, "Json Error");
}
// For testing to make sure i have all files in box
printFilesInBox();
}
I'm attempting to create a PDF file from an HTML file. After looking around a little I've found: wkhtmltopdf to be perfect. I need to call this .exe from the ASP.NET server. I've attempted:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = HttpContext.Current.Server.MapPath("wkhtmltopdf.exe");
p.StartInfo.Arguments = "TestPDF.htm TestPDF.pdf";
p.Start();
p.WaitForExit();
With no success of any files being created on the server. Can anyone give me a pointer in the right direction? I put the wkhtmltopdf.exe file at the top level directory of the site. Is there anywhere else it should be held?
Edit: If anyone has better solutions to dynamically create pdf files from html, please let me know.
Update:
My answer below, creates the pdf file on the disk. I then streamed that file to the users browser as a download. Consider using something like Hath's answer below to get wkhtml2pdf to output to a stream instead and then send that directly to the user - that will bypass lots of issues with file permissions etc.
My original answer:
Make sure you've specified an output path for the PDF that is writeable by the ASP.NET process of IIS running on your server (usually NETWORK_SERVICE I think).
Mine looks like this (and it works):
/// <summary>
/// Convert Html page at a given URL to a PDF file using open-source tool wkhtml2pdf
/// </summary>
/// <param name="Url"></param>
/// <param name="outputFilename"></param>
/// <returns></returns>
public static bool HtmlToPdf(string Url, string outputFilename)
{
// assemble destination PDF file name
string filename = ConfigurationManager.AppSettings["ExportFilePath"] + "\\" + outputFilename + ".pdf";
// get proj no for header
Project project = new Project(int.Parse(outputFilename));
var p = new System.Diagnostics.Process();
p.StartInfo.FileName = ConfigurationManager.AppSettings["HtmlToPdfExePath"];
string switches = "--print-media-type ";
switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm ";
switches += "--page-size A4 ";
switches += "--no-background ";
switches += "--redirect-delay 100";
p.StartInfo.Arguments = switches + " " + Url + " " + filename;
p.StartInfo.UseShellExecute = false; // needs to be false in order to redirect output
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true; // redirect all 3, as it should be all 3 or none
p.StartInfo.WorkingDirectory = StripFilenameFromFullPath(p.StartInfo.FileName);
p.Start();
// read the output here...
string output = p.StandardOutput.ReadToEnd();
// ...then wait n milliseconds for exit (as after exit, it can't read the output)
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
// if 0 or 2, it worked (not sure about other values, I want a better way to confirm this)
return (returnCode == 0 || returnCode == 2);
}
I had the same problem when i tried using msmq with a windows service but it was very slow for some reason. (the process part).
This is what finally worked:
private void DoDownload()
{
var url = Request.Url.GetLeftPart(UriPartial.Authority) + "/CPCDownload.aspx?IsPDF=False?UserID=" + this.CurrentUser.UserID.ToString();
var file = WKHtmlToPdf(url);
if (file != null)
{
Response.ContentType = "Application/pdf";
Response.BinaryWrite(file);
Response.End();
}
}
public byte[] WKHtmlToPdf(string url)
{
var fileName = " - ";
var wkhtmlDir = "C:\\Program Files\\wkhtmltopdf\\";
var wkhtml = "C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe";
var p = new Process();
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = wkhtml;
p.StartInfo.WorkingDirectory = wkhtmlDir;
string switches = "";
switches += "--print-media-type ";
switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm ";
switches += "--page-size Letter ";
p.StartInfo.Arguments = switches + " " + url + " " + fileName;
p.Start();
//read output
byte[] buffer = new byte[32768];
byte[] file;
using(var ms = new MemoryStream())
{
while(true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0,buffer.Length);
if(read <=0)
{
break;
}
ms.Write(buffer, 0, read);
}
file = ms.ToArray();
}
// wait or exit
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
return returnCode == 0 ? file : null;
}
Thanks Graham Ambrose and everyone else.
OK, so this is an old question, but an excellent one. And since I did not find a good answer, I made my own :) Also, I've posted this super simple project to GitHub.
Here is some sample code:
var pdfData = HtmlToXConverter.ConvertToPdf("<h1>SOO COOL!</h1>");
Here are some key points:
No P/Invoke
No creating of a new process
No file-system (all in RAM)
Native .NET DLL with intellisense, etc.
Ability to generate a PDF or PNG (HtmlToXConverter.ConvertToPng)
Check out the C# wrapper library (using P/Invoke) for the wkhtmltopdf library: https://github.com/pruiz/WkHtmlToXSharp
There are many reason why this is generally a bad idea. How are you going to control the executables that get spawned off but end up living on in memory if there is a crash? What about denial-of-service attacks, or if something malicious gets into TestPDF.htm?
My understanding is that the ASP.NET user account will not have the rights to logon locally. It also needs to have the correct file permissions to access the executable and to write to the file system. You need to edit the local security policy and let the ASP.NET user account (maybe ASPNET) logon locally (it may be in the deny list by default). Then you need to edit the permissions on the NTFS filesystem for the other files. If you are in a shared hosting environment it may be impossible to apply the configuration you need.
The best way to use an external executable like this is to queue jobs from the ASP.NET code and have some sort of service monitor the queue. If you do this you will protect yourself from all sorts of bad things happening. The maintenance issues with changing the user account are not worth the effort in my opinion, and whilst setting up a service or scheduled job is a pain, its just a better design. The ASP.NET page should poll a result queue for the output and you can present the user with a wait page. This is acceptable in most cases.
You can tell wkhtmltopdf to send it's output to sout by specifying "-" as the output file.
You can then read the output from the process into the response stream and avoid the permissions issues with writing to the file system.
My take on this with 2018 stuff.
I am using async. I am streaming to and from wkhtmltopdf. I created a new StreamWriter because wkhtmltopdf is expecting utf-8 by default but it is set to something else when the process starts.
I didn't include a lot of arguments since those varies from user to user. You can add what you need using additionalArgs.
I removed p.WaitForExit(...) since I wasn't handling if it fails and it would hang anyway on await tStandardOutput. If timeout is needed, then you would have to call Wait(...) on the different tasks with a cancellationtoken or timeout and handle accordingly.
public async Task<byte[]> GeneratePdf(string html, string additionalArgs)
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = #"C:\Program Files\wkhtmltopdf\wkhtmltopdf.exe",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = "-q -n " + additionalArgs + " - -";
};
using (var p = Process.Start(psi))
using (var pdfSream = new MemoryStream())
using (var utf8Writer = new StreamWriter(p.StandardInput.BaseStream,
Encoding.UTF8))
{
await utf8Writer.WriteAsync(html);
utf8Writer.Close();
var tStdOut = p.StandardOutput.BaseStream.CopyToAsync(pdfSream);
var tStdError = p.StandardError.ReadToEndAsync();
await tStandardOutput;
string errors = await tStandardError;
if (!string.IsNullOrEmpty(errors)) { /* deal/log with errors */ }
return pdfSream.ToArray();
}
}
Things I haven't included in there but could be useful if you have images, css or other stuff that wkhtmltopdf will have to load when rendering the html page:
you can pass the authentication cookie using --cookie
in the header of the html page, you can set the base tag with href pointing to the server and wkhtmltopdf will use that if need be
Thanks for the question / answer / all the comments above. I came upon this when I was writing my own C# wrapper for WKHTMLtoPDF and it answered a couple of the problems I had. I ended up writing about this in a blog post - which also contains my wrapper (you'll no doubt see the "inspiration" from the entries above seeping into my code...)
Making PDFs from HTML in C# using WKHTMLtoPDF
Thanks again guys!
The ASP .Net process probably doesn't have write access to the directory.
Try telling it to write to %TEMP%, and see if it works.
Also, make your ASP .Net page echo the process's stdout and stderr, and check for error messages.
Generally return code =0 is coming if the pdf file is created properly and correctly.If it's not created then the value is in -ve range.
using System;
using System.Diagnostics;
using System.Web;
public partial class pdftest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
private void fn_test()
{
try
{
string url = HttpContext.Current.Request.Url.AbsoluteUri;
Response.Write(url);
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName =
#"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe";//"wkhtmltopdf.exe";
startInfo.Arguments = url + #" C:\test"
+ Guid.NewGuid().ToString() + ".pdf";
Process.Start(startInfo);
}
catch (Exception ex)
{
string xx = ex.Message.ToString();
Response.Write("<br>" + xx);
}
}
protected void btn_test_Click(object sender, EventArgs e)
{
fn_test();
}
}