I'm downloading and saving images from URL. The image can be of any format.
Right click on the image, select Properties and then the Details tab. There is a Comments field. I would like to add text in that field while saving the image . Is it possible.
using (WebClient webClient = new WebClient())
{
webClient.DownloadFile("http://www.example.com/1.jpg", "1.jpg");
}
The property system in Windows has a complex story, you can find more information about it here: Extracting Windows File Properties (Custom Properties) C#
You could try it with the Microsoft.WindowsAPICodePack.Shell nuget package:
var file = ShellFile.FromFilePath("your image.jpg");
using (var writer = file.Properties.GetPropertyWriter())
{
writer.WriteProperty(file.Properties.System.Comment, "hello");
}
But, in the case of images, the shell may not be able to write extra properties on the file itself (most imaging codecs except JPG don't support such a 'comments' metadata). What I suggest in this case is use this CodeFluentRuntime nuget package's CompoundStorage class, here is an example:
static void Main(string[] args)
{
var storage = new CompoundStorage("your image.png", false); // open for write
storage.Properties.Comments = "hello"; // change well-known "comments" property
storage.CommitChanges();
}
It will work and write the property information not stricly on the file, but on NTFS (it works also on pure plain .txt files for example). If you read the file again and browse all properties, like this, you should see it:
static void Main(string[] args)
{
var storage = new CompoundStorage("consent.png"); // open for read
foreach (var prop in storage.Properties)
{
Console.WriteLine(prop.Name + "=" + prop.Value);
}
}
Now, the problem is, with recent versions of Windows, the shell property sheet you show in your question will not display the Comments property (for example, I'm running on Windows 10 and I don't see it although it's properly written). Unfortunately, there is not many other options for images files.
You can use this reference link to solved your problem.
http://www.codeproject.com/Articles/3615/File-Information-using-C
Related
I have a requirement to insert a unique ID into image files without modifying the image content – ie it’s just the metadata that I want to modify. I’m starting with the JPEG files because there is an appropriate EXIF property available: ImageUniqueID.
I’m using C# with .NET Core 3.1 for this exercise with ImageSharp.
I can change the EXIF data with the ImageSharp using the following code (show simplified without existing record checks, etc):
using (var image = Image.Load(filename))
{
var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
image.Metadata.ExifProfile.SetValue(ExifTag.ImageUniqueID, imageUniqueIdentifier);
var jpegEncoder = new JpegEncoder() { Quality = 100 };
image.Save(filename, jpegEncoder);
}
I did play with the Quality setting in the JpegEncoder, but was still getting either unacceptable quality degradation or file size increases.
Is there a way of just reading the meta data, altering it and then writing it back without affecting the image at all?
I also looked at MetadataExtractor.NET but this doesn’t have a write facility and would happily look at other .NET Core methods or libraries.
After some research I've found that there is ExifLibrary which allow you to modify only image metadata.
Documentation (examples included)
Example how to add unique image id for jpg file:
var file = ImageFile.FromFile("path_to_jpg_file");
var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
file.Properties.Set(ExifLibrary.ExifTag.ImageUniqueID, imageUniqueIdentifier);
file.Save("path_to_jpg_file");
Nuget package: ExifLibNet.
Here is some code that just needs .NET with PresentationCore and WindowsBase. The underlying technology that WPF uses is WIC (Windows Imaging Component). WIC has full support for image metadata.
EXIF's ImageUniqueID is handled specifically as a Windows Property named System.Image.ImageID
Some other properties such as System.Photo.CameraModel can be seen directly in Windows Explorer detailed views if you add the corresponding column "Camera Model", but not System.Image.ImageID, AFAIK.
// needs PresentationCore & WindowsBase references
var frame = BitmapDecoder.Create(new Uri("test1.jpg", UriKind.RelativeOrAbsolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];
// create encoder, add frame, we need to copy since we want to update metadata
var encoder = BitmapEncoder.Create(frame.Decoder.CodecInfo.ContainerFormat);
var copy = BitmapFrame.Create(frame);
// get frame metadata
var metaData = (BitmapMetadata)copy.Metadata;
// show existing System.Image.ImageID (if any)
Console.WriteLine("ImageUniqueID: " + metaData.GetQuery("System.Image.ImageID"));
// for some reason, we can't use "System.Image.ImageID" to set the meta data
// so use the "Metadata Query Language"
metaData.SetQuery("/app1/ifd/exif/{ushort=42016}", "My Super ID");
// write file back
encoder.Frames.Add(copy);
using (var stream = File.OpenWrite("test1copy.jpg"))
{
encoder.Save(stream);
}
So, the title may be misleading. I am building an android app that reads information from a text file, which is located on a cloud server (I would prefer to use either OneDrive, DropBox, or Google Drive [whichever is easiest]; others are fine). Periodically, the program will write information to the text file, still located on the cloud server. So, my question is twofold: Is it possible to read and write to a text file that is located on a cloud server? If so, how in the world would I complete this task? I have noticed the use of WebClient but I can't find a reasonable method or explanation on how this works. This program is coded in C#. This is what I have so far:
private string filename = "datafile.txt";
private List<Category> myList; //A list of an object that I developed ('Category')
//Allow the user interface to handle the error
public void readDatabase() {
//Here is where the magic has to occur, in order to read the file
...
//The usual reader that I use to read standard text files
StreamReader fileReader = new StreamReader(filename);
string line = "";
while ((line = fileReader.ReadLine()) != null)
//convertToCategory is my private method to convert the string to
myLine.Add(convertToCategory(line);
fileReader.close();
}
public void writeDatabase() {
//Here is where the magic has to occur, in order to write to the file
...
//The usual writer that I use to write standard text files
StreamWriter fileWriter = new StreamWriter(filename);
for (int i = 0; i < this.myList.Count; i++)
//toString() is something was developed in my object called 'Category'
fileWriter.WriteLine(fileWriter[i].toString());
fileWriter.close();
}
I would love to use Google Drive as my cloud server, but I am open to other possibilities, if necessary. I just want an easy and efficient method to read/write to the text file.
Possible Implementations:
Have seen possible solutions, where the file is downloaded locally and then read like normal and then uploaded at time of closing. However, if I could get away with it, I don't want the text file to be downloaded.
I have, also, seen several places where a SQL database is used in this instance. But the unfortunate thing is that I don't have any knowledge in developing with SQL. So, using a SQL server would be ideal (because speed is very important for this application) but it will be difficult for me to understand how it works.
I've had some problem when copying content from a Word document to another Word document.
The document where the information should end up in have a header.
So far I have managed to copy the content to the second document and not affecting the header.
However I can't figure out how to bind the relationships for links and Images.
This is my code so far:
public static void AddContentToTemplateCopy(
string sourceDocumentPath, string endDocumentPath)
{
using (WordprocessingDocument sourceDoc =
WordprocessingDocument.Open(sourceDocumentPath, false))
using (WordprocessingDocument endDoc =
WordprocessingDocument.Open(endDocumentPath, true))
{
var sourceMainPart = sourceDoc.MainDocumentPart;
var sourceBody = sourceMainPart.Document.Body;
var endSection = endDoc.MainDocumentPart.Document.Body.Elements<SectionProperties>();
var endDocMainPart = endDoc.MainDocumentPart;
var sourceBodyClone = sourceBody.CloneNode(true);
sourceBodyClone.ReplaceChild(endSection.FirstOrDefault().CloneNode(true), sourceBodyClone.Elements<SectionProperties>().FirstOrDefault());
endDocMainPart.Document.ReplaceChild(sourceBodyClone, endDocMainPart.Document.Body);
foreach (HyperlinkRelationship link in sourceMainPart.HyperlinkRelationships)
{
endDocMainPart.AddHyperlinkRelationship(link.Uri, link.IsExternal, link.Id);
}
}
I get the following Error : 'rId6' ID conflicts with the ID of an existing relationship for the specified source.
And the if i have a Image in the content it can't be displayed.
If I zip the document and look at the files in the package I can find the Image but for the same reason as the links the Relation
So my question is: How do I bind the links and Images with their "_rels" references? or how do I copy them so that it works..
This is a Relationship link when I have added the link by hand.
<Relationship Target="media/image1.jpg" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Id="rId11"/>
A picture to show that the link text is copied but have no formatting and that the image can't be displayed.
Thanks to the answer by JasonPlutext i managed to use OpenXML PowerTools (Version 2.2). Keep in mind that the .Net version is 3.5 when importing the project. You Might need to change it. (Supports Open XML 2.5 as well from what I've noticed)
Very simple to create new documents and take parts from old documents.
The code here is in my case where I want the formatting and content from one and then the Header from a template document. The order matters.
Hopefully this will save time for others with the same problem.
public static void AddContentToTemplateCopy(string templateDocumentPath,
string contentDocumentPath,
List<Source> sources,
string outName)
{
sources = new List<Source>()
{
new Source(new WmlDocument(contentDocumentPath),false),
new Source(new WmlDocument(templateDocumentPath),true),
};
DocumentBuilder.BuildDocument(sources, outName);
}
You might find it easier to try Eric White's document builder.
Using Sitecore 6.5, when images are rendered on a web page, a URL such as the one below is used
~/media/OSS/Images/MyImage
But if you add an image from the library in a content editor a path such as below is used
~/media/1CFDDC34C94E460FAA2B1518DCA22360.PNG
This makes sense as it's trying to use a meaningful path when rendered for the web.
We would like to use the first media image path to add images in the content editor in HTML view rather than the default second method. This is because we are actually taking some html files and automatically adding them in to Sitecore via a script and we can change the image paths to a location in the media library if the first image format is used by using a convention so the images should appear in the newly created items. We have now idea about a media library image ID.
The first format does appear to work as images are rendered in the content editor design editor and when the page is rendered but Sitecore marks these as broken links in the Content Editor. Are any ideas on whether we are safe to use this format?
You may want to avoid hard coding paths to media in the rich text field. The second "dynamic link" is an important feature of Sitecore in that it keeps a connection between the media and item in the Links database. This safeguards you if you ever delete or move the media.
Since it sounds like you are importing content from an external source and you already have a means of detecting the image paths, I would recommend (if possible) that you upload the images programmatically and insert the dynamic links.
Below is a function that you can call for uploading to the Media Library and getting back the media item:
Example usage:
var file = AddFile("/assets/images/my-image.jpg", "/sitecore/media library/images/example", "my-image");
The code:
private MediaItem AddFile(string relativeUrl, string sitecorePath, string mediaItemName)
{
var extension = Path.GetExtension(relativeUrl);
var localFilename = #"c:\temp\" + mediaItemName + extension;
using (var client = new WebClient())
{
client.DownloadFile("http://yourdomain.com" + relativeUrl, localFilename);
}
// Create the options
var options = new MediaCreatorOptions
{
FileBased = false,
IncludeExtensionInItemName = false,
KeepExisting = false,
Versioned = false,
Destination = sitecorePath + "/" + mediaItemName,
Database = Factory.GetDatabase("master")
};
// Now create the file
var creator = new MediaCreator();
var mediaItem = creator.CreateFromFile(localFilename, options);
return mediaItem;
}
As for generating the dynamic link to the media, I actually haven't found a Sitecore method to do this, so I resorted to the following code:
var extension = !String.IsNullOrEmpty(Settings.Media.RequestExtension)
? Settings.Media.RequestExtension
: ((MediaItem)item).Extension;
var dynamicMediaUrl = String.Format(
"{0}{1}.{2}",
MediaManager.MediaLinkPrefix,
item.ID.ToShortID(),
extension);
No it will not cause any rendering issue apart from the broken links notification as you noted. Also when you select an image in the editor and select to edit the media folder will be at the root rather than at the image itself. But as Derek has noted, the use of dynamic links is an important feature to make sure your links do not break if something is moved or deleted.
I would add to his answer that since you are adding the text via a script you can detect images in the text using HtmlAgilityPack (already used in Sitecore) or FizzlerEx (more similar to jQuery syntax), use the code he provided to upload the images to the media library, grab the GUID and replace the src. Something along the lines of:
string content = "<whatever your html to go in the rich text field>";
HtmlDocument doc = new HtmlDocument();
doc.Load(content);
foreach(HtmlNode img in doc.DocumentElement.SelectNodes("//img[starts-with(#src, '/media/')]")
{
HtmlAttribute attr = img["src"];
Item scMediaItem = UploadLocalMedia(attr.Value);
attr.Value = GetDynamicMediaUrl(scMediaItem);
}
I have a text file with a list of 300,000 words and the frequency with wich they occur. Each line is in the format Word:FequencyOfOccurence.
I want this information to be accessible from within the C# code. I can't hard code the list since it is too long, and I'm not sure how to go about accessing it from a file on the server. Ideally I'd ideally like the information to be downloaded only if it's used (To save on bandwidth) but this is not a high priority as the file is not too big and internet speeds are always increasing.
It doesn't need to be useable for binding.
The information does not need to be editable once the project has been built.
Here is another alternative. Zip the file up and stick it in the clientBin folder next to the apllication XAP. Then at the point in the app where the content is needed do something like this:-
public void GetWordFrequencyResource(Action<string> callback)
{
WebClient client = new WebClient();
client.OpenReadAsync += (s, args) =>
{
try
{
var zipRes = new StreamResourceInfo(args.Result, null)
var txtRes = Application.GetResourceStream(zipRes, new Uri("WordFrequency.txt", UriKind.Relative));
string result = new StreamReader(txtRes.Stream).ReadToEnd();
callback(result);
}
catch
{
callback(null); //Fetch failed.
}
}
client.OpenReadAsync(new Uri("WordFrequency.zip", UriKind.Relative"));
}
Usage:-
var wordFrequency = new Dictionary<string, int>();
GetWordFrequencyResource(s =>
{
// Code here to burst string into dictionary.
});
// Note code here is asynchronous with the building of the dictionary don't attempt to
// use the dictionary here.
The above code allows you to store the file in an efficient zip format but not in the XAP itself. Hence you can download it on demand. It makes use of the fact that a XAP is a zip file so Application.GetResourceStream which is designed to pull resources from XAP files can be used on a zip file.
BTW, I'm not actually suggesting you use a dictionary, I'm just using a dictionary as simple example. In reality I would imagine the file is in sorted order. If that is the case you could use a KeyValuePair<string, int> for each entry but create a custom collection type that holds them in an array or List and then use some Binary search methods to index into it.
Based on your comments, you could download the word list file if you are required to have a very thin server layer. The XAP file containing your Silverlight application is nothing more than a ZIP file with all the referenced files for your Silverlight client layer. Try adding the word list as content that gets compiled into the XAP and see how big the file gets. Text usually compresses really well. In general, though, you'll want to be friendly with your users in how much memory your application consumes. Loading a huge text file into memory, in addition to everything else you need in your app, may untimately make your app a resource hog.
A better practice, in general, would be to call a web service. The service could would perform whatever look up logic you need. Here's a blog post from a quick search that should get you started: (This was written for SL2, but should apply the same for SL3.)
Calling web services with Silverlight 2
Even better would be to store your list in a SQL Server. It will be much easier and quicker to query.
You could create a WCF service on the server side that will send the data to the Silverlight application. Once you retrieve the information you could cache it in-memory inside the client. Here's an example of calling a WCF service method from Silverlight.
Another possibility is to embed the text file into the Silverlight assembly that is deployed to the client:
using (var stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("namespace.data.txt"))
using (var reader = new StreamReader(stream))
{
string data = reader.ReadToEnd();
// Do something with the data
}