Is there a function that takes a filename from the Image object - c#

I'm using the Movie Maker Library Timeline SDK Control 6.0 dll
And to add a photo you need a STRING of a file name. So far everything is fine
But I want to insert a function that gets an IMAGE object
But the library does not have a function that cables an IMAGE object
What I need is to get the file name out of the IMAGE object
That is: string fileName = image
Image img = default;
using (WebClient client = new WebClient())
{
string url = textBox1.Text;
Stream stream = client.OpenRead(url);
img = Image.FromStream(stream);
axTimelineControl1.AddImageClip(trackIndex: 1, fileName :img.ToString(),
clipStartTime: axTimelineControl1.GetMediaDuration(img.ToString()), clipStopTime: 4);
}

You've got a small bit of an "XY problem" here--you're asking the wrong question. Your axTimelineControl1 expects an image's file name. This implies that it also expects there to be an image saved to disk with that file name.
But all you have is a remote image, behind some URL. client.OpenRead(url) downloads the image into a Stream, but you can't do anything with that directly.
So, you don't want to take that image, and put it into a WinForms Image object. Instead, you want to save that image to disk with a file name, and then give that file name to your axTimelineControl1.
You have a few options for doing that:
1) You could take the Stream you got from client.OpenRead(), turn it into a FileStream and save it to disk.
2) You could use the WebClient to download the image directly to disk, and then give the image's file name to the axTimelineControl1.
Let's do 2) instead. It'll save a few steps.
First, create the file.
string fileName = System.IO.Path.GetTempFileName();
System.IO.File.Create(fileName).Close();
We're creating a "Temp" file here--these are meant to be treated as disposable. Note that Windows doesn't clean them up for you, so once you're done with it, your program should delete it. System.IO.File.Create() gives us a FileStream object, but we don't need it, so we Close() it right away, so that the WebClient will be able to write to our file.
Next, we download our image, and tell WebClient to save it to our newly-created Temp file:
// Defining my own URL here. Feel free to substitute your own.
string url = "https://derpicdn.net/img/view/2018/5/18/1735426.jpeg";
using (var client = new WebClient())
{
client.DownloadFile(url, fileName);
}
Now we have an image on disk, and we can tell the Movie Maker SDK Control where to find it:
float duration = axTimelineControl1.GetMediaDuration(fileName);
axTimelineControl1.AddImageClip(
trackIndex: 1,
fileName: fileName,
clipStartTime: duration,
clipStopTime: 4);
And that should do it.
The entire code listing:
string fileName = System.IO.Path.GetTempFileName();
System.IO.File.Create(fileName).Close();
// Defining my own URL here. Feel free to substitute your own.
string url = "https://derpicdn.net/img/view/2018/5/18/1735426.jpeg";
using (var client = new WebClient())
{
client.DownloadFile(url, fileName);
}
float duration = axTimelineControl1.GetMediaDuration(fileName);
axTimelineControl1.AddImageClip(
trackIndex: 1,
fileName: fileName,
clipStartTime: duration,
clipStopTime: 4);
Don't forget to clean up your temp file!

Related

C# Image.Save() cause Cannot access a closed Stream when saving JPG file (PNG works)

I've got strange behavior.
I have a code, when I am sending byte[] array to the Image (sometimes png, sometimes jpg).
When I save PNG format - everything is ok, but when I am trying to save JPG, there is an exception: "Cannot access a closed Stream"
Code:
imgTarget.Save(wwwroot + "\\ImageBank\\" + TaskRunID + "_" + TestCaseID + "_" + TestDataID + "_" + ImageCounter + Extension, ImageFormat.Jpeg);
What I have checked:
imgTarget is not null
imgTarget contains correct data (even RawFormat is Png or Jpeg as It should)
imgTarget.Save() throws an exception: System.ObjectDisposedException: 'Cannot access a closed Stream.'
I have tried to use Bitmap - copy imgTarget to new Bitmap and then Save (with the same result)
I have tried to call Save() method with the ImageFormat parameter correctly set to jpeg or png (with the same result) - for both for Image.Save() and Bitmap.Save()
I have checked the correct path and file name (it's ok) (in this case it is C:\MyProd\wwwroot\ImageBank\10611_8_-1_1.jpeg)
Strange is that imgTarget contains data just before calling the method Save() - but right after it is null/disposed...
Anyone any ideas?
EDIT:
I have prepared little bit of code which is failing in the same way - it fails at last line .Save()
// This contains only the URL for the downloading of the file
string url = $"***url to download jpg file***";
// Request for the API - which downloads the jpg file via GET and provide the RawData via Response.RawBytes
APIRequest request = new APIRequest(RestSharp.Method.GET, url, new Authentication("user", "password", "-1"));
// Test case is only class which calls RestSharp (get png or jpg file)
TestCase t = new TestCase();
// This downloads the jpg file and store it as byte[] in t.GetDataFromAPI
t.API(request);
// Using downloaded data as byte[]
byte[] APIImageSource = t.GetDataFromAPI;
// Default extensions for saving files is .png
string Extension = ".png";
Image imgTarget;
// Now I use bytes[] and convert them into the image
imgTarget = ConvertBytesToImage(APIImageSource);
if (t.GetImageFormat(APIImageSource) == ExpectedFormat.JPEG)
{
Extension = ".jpeg";
}
string path = "C:\\Temp\\filename" + Extension;
imgTarget.Save(path + Extension, ImageFormat.Jpeg);
There is Method ConvertBytesToImage:
internal static Image ConvertBytesToImage(byte[] bytes)
{
using (var ms = new MemoryStream(bytes))
{
return Image.FromStream(ms);
}
}
Comment from #Simon Mourier is the solution:
remove the using on the MemoryStream, or remove that ConvertBytesToImage and keep the MemoryStream alive until you completely saved the image. – Simon Mourier 1 min ago
I don't understand why, but I have removed the using statement and now it works.
Thank all of You

How to manually store image in (database) table in Visual Studio 2012 and retrieve using jQuery Ajax in ASP.NET MVC

My database table
First I tried storing images with datatype image/varbinary but I get an error:
Invalid Value The changed value in this cell was not recognized as being valid. .Net Framework Data Type: Byte[] Error Message: You cannot use the result pane to set this Field data to values other than NULL Type a value appropriate for the data type or press ESC to cancel the change
How can I store image in my database table manually and retrieve them through jQuery Ajax method?
use in C# Button Submit with data source
if (FileUpload1.PostedFile != null && FileUpload1.PostedFile.FileName != null)
{
byte[] img = new byte[Request.Files[0].ContentLength];
Request.Files[0].InputStream.Read(img, 0, Request.Files[0].ContentLength);
datasource.Photo = img; --here you can save file to Database
}
for retrieving image from dbserver use belowed
img.ImageUrl = "data:image/jpg;base64," + Convert.ToBase64String(Upprofile.Photo);
Have you ever tried other ways to store images ?
I have never used such a way to store image in database if you want my two cents it would be better to save your image on host and save its url in the database.
You only need url to show picture in browser...!
I'm not sure if this works in VS2012, but you can try this approach that I'm using in my MVC projects:
public action UploadFile(HttpPostedFileBase file) //this is how you get the file that was submited from your form
{
//so now that I have the file, i need to save it somewhere
//let's see if we have a folder for that, if not let's create it
if(!Directory.Exists(Server.MapPath("~/The path from the root of the app to the uploaded files folder/")))
{
Directory.CreateDirectory(Server.MapPath("~/The path from the root of the app to the uploaded files folder/"));
} //the ~ symbol is part of the code, it returns the physical path to the root of the application
var storage = Server.MapPath("~/The path from the root of the app to the uploaded files folder/");
//now we need a name, we can generate one as a GUID for example, or we can get it's own.
var fileName = file.FileName.Substring(file.FileName.LastIndexOf("\"), file.Name.Length + 1);
//Now you might have to play a bit with the substring arguments, I'm not sure i wrote that right,
//it might not need the +1 part, or it might need it on the LastIndexOf as well,
//anyway, now we have the name and the folder where to save our file, let's do that:
path = String.Format("{0}\{1}", storage, filename);
try
{
file.SaveAs(path); //Now the file is on the server,
//just save this path and whatever else you need in the database so you can
//grab it later.
}
catch
{
//do your exception handling here
}
Saving to database here...
}
In order to grab it you just get that object from the database like any other objects like so:
Let's presume you're doing everything in your controller, which you shouldn't,
public Action GetFile(int id)
{
using(context = new YourDbContext())
{
var file = context.Files.FirstOrDefault(p => p.Id == id);
}
return View(file);
}
And put it's path for the src in html to show the image like so:
<img src="#Model.path" /> //Considering that in your controller you will `return View(file);`
//So your file object is the model.
One more thing, I'm not sure if in paths you'll need \ or /, I always get it wrong the first time so debug it on the first run and if your path looks likeFolder1Folder2Folder3File it means you need to change it :).
I hope this helped, good luck.
P.S., there might be better ways to do it, but this is how I'm doing it and it seams to work fine for me.
On server, you convert image to a base64 string and save to database.
using (Image image = Image.FromFile(Path))
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
byte[] imageBytes = m.ToArray();
// Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes);
return base64String;
}
}
Beside Client, you put base64 string in Image object
var image = new Image();
image.src = 'data:image/png;base64,your base64 string';
document.body.appendChild(image);
I think store image on the disk better.

Conversion og BMP instance to png

I have function name uploadLayerIcons which is as follows:
private void uploadLayerIcon(string LayerName)
{
Bitmap icon= new Bitmap(#"C:\Users\HP\Desktop\911\Prism\Prism_Resources\m.png");
System.IO.MemoryStream stream = new System.IO.MemoryStream();
icon.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] imageBytes = stream.ToArray();
// Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes);
HttpWebRequest m_ObjRequest; //Request which needed to be sent to server
HttpWebResponse m_ObjResponse; // Response which is sent back from the server to the client
StreamReader reader = null; // making a stream reader to read the web pageand initialize it to null
string m_Url = "http://192.168.1.30/muneem/erp/uploadIcon.php" + "?bitmap=" + base64String + "&layerName=" + LayerName; // the url of that web page
string m_Response = "";
m_ObjRequest = (HttpWebRequest)WebRequest.Create(m_Url); // creating the url and setting the values
m_ObjRequest.Method = "GET";
m_ObjRequest.ContentType = "application/json; charset=utf-8";
//m_ObjRequest.ContentLength = 500;
m_ObjRequest.KeepAlive = false;
m_ObjResponse = (HttpWebResponse)m_ObjRequest.GetResponse(); // getting response from the server
using (reader = new StreamReader(m_ObjResponse.GetResponseStream())) // using stream reader to read the web page
{
m_Response = reader.ReadToEnd();
reader.Close(); // Close the StreamReader
}
m_ObjResponse.Close();
m_ObjRequest = null;
m_ObjResponse = null;
}
UploadIcon.php file is as follows:
<?php
$bitmap=$_GET['bitmap'];
$name=$_GET['layerName'];
$data = base64_decode($bitmap);
$filepath="app/uams/uploadedImages/".$name.".jpg";
file_put_contents($filepath,$data);
?>
Its not converting correctly the same image which i have sent to server.
I have search on internet many thing but all in vain. I have also tried this thing
Bitmap icon= new Bitmap(#"C:\Users\HP\Desktop\911\Prism\Prism_Resources\m.png");
icon.save("Path of srrver")
But its not working.
So, you are doing it pretty much wrong. First of all, if you change the extension of the file to .jpg it does not automagically become jpg image.
So, what I suggest you to do is to send the raw png data instead of bitmap, and then using something like this in php:
<?
$imagedata = $_POST["data"];
$im = imagecreatefromstring($imagedata);
$filepath="app/uams/uploadedImages/image.jpg";
imagejpeg($im,$filepath);
?>
Also, as pointed out in previous answer by #DoXicK, do not send file by GET method, you should post it instead, and that is what this example is based on.
PHP's function imagecreatefromstring identifies the image type, and creates the gdlib object accordingly (but it does not work very well with bitmaps). That is why I suggested that you use raw png data instead of converting it to bitmap. Also, bitmap data is unneccesary large for transfer.
For imagecreatefromstring to work you need GD Library installed and enabled. To see if it is enabled create an empty file (named for example info.php) and inside it put only
<?
phpinfo();
?>
If you see GD Support set to Enable on the page, when you open the file, you have gdlib enabled. If you do not see it, do the following:
On windows find ;extension=php_gd2.dll in php.ini file of your php installation, and uncomment it (remove ; from the beginning) so it now is extension=php_gd2.dll and then restart Apache.
On linux you need to do sudo apt-get install php5-gd and then restart Apache.
you are loading a PNG to BMP file format
You are sending a file by GET
you are saving the BMP file as JPG te minute you receive the BMP
So:
Don't open as PNG as BMP. open a PNG as PNG as it is either smaller or the same size. There is no need for the BMP here...
POST it
Just because you call it a JPG, doesn't make it a JPG. It currently is a BMP, saved to a JPG.
IF it even saves a .jpg file, it is a .bmp file with the wrong extension.

C# Save image without using Dialog

I am having trouble saving a user uploaded image to my "logos" file when the user presses "Save Team" on my form.
For you to get a taster of what the application does I have provided a screenshot here, upon pressing add team all the text box data is written to a text file and I want the image to automatically save into a predefined folder "logos", however I get the GDI++ error whenever I press save.
After reading through a few old threads on the web, I found that it could be caused by a few things such as file permissions, file size, the files name or even an open stream.
Below is the code I am currently using to save out my file which prompts the error:
private void btnAddTeam_Click(object sender, EventArgs e)
{
//VALIDATION FOR TEXT FILE AND OTHER STUFF HERE...
//SAVE THE IMAGE FILE
string filePath = picTeamLogo.ImageLocation;
Image tempImage = Image.FromFile(filePath);
tempImage.Save(#"../logos/");
}
If you are viewing the screenshot, please do not get confused by "arsenal.png" I am aware that that is not the full file path, its conversion is handled in another method, as only the filename is required to be written to the text file.
If anybody has any ideas on where I am going wrong then please point me in the right direction, vague errors such as the GDI one I just received are such a headache!
Alex.
Try this method
public void storeFile(FileUpload File, string dirName)
{
if (!Directory.Exists(MapPath("~/uploads/" + dirName)))
{
Directory.CreateDirectory(MapPath("~/uploads/" + dirName));
}
string saveLocation = MapPath("~/uploads/" + dirName + "/" + Path.GetFileName(File.FileName));
File.SaveAs(saveLocation);
}
that's because you have not specified the file name for save function !
take a look at this :
string filePath = picTeamLogo.ImageLocation;
FileInfo fi = new FileInfo(filePath);
Image tempImage = Image.FromFile(fi.FullName);
tempImage.Save(#"../logo/" + fi.Name);
now it works properly
See this earlier post of mine for similar issue...
A Generic error occurred in GDI+ in Bitmap.Save method
The stream remains locked - you can use memory stream as a temp location then save.
When either a Bitmap object or an Image object is constructed from a
file, the file remains locked for the lifetime of the object. As a
result, you cannot change an image and save it back to the same file
where it originated.
http://support.microsoft.com/?id=814675
string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
tempImage.Save(memory, ImageFormat.Jpeg);
// memory.ToStream(fs) // I think the same
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
Or alternative is loading image via MemoryStream into the image - so the stream doesn't get locked in the first place...

How can I form a Word document using stream of bytes

I have a stream of bytes which actually (if put right) will form a valid Word file, I need to convert this stream into a Word file without writing it to disk, I take the original stream from SQL Server database table:
ID Name FileData
----------------------------------------
1 Word1 292jf2jf2ofm29fj29fj29fj29f2jf29efj29fj2f9 (actual file data)
the FileData field carries the data.
Microsoft.Office.Interop.Word.Application word = new Microsoft.Office.Interop.Word.Application();
Microsoft.Office.Interop.Word.Document doc = new Microsoft.Office.Interop.Word.Document();
doc = word.Documents.Open(#"C:\SampleText.doc");
doc.Activate();
The above code opens and fill a Word file from File System, I don't want that, I want to define a new Microsoft.Office.Interop.Word.Document, but I want to fill its content manually from byte stream.
After getting the in-memory Word document, I want to do some parsing of keywords.
Any ideas?
Create an in memmory file system, there are drivers for that.
Give word a path to an ftp server path (or something else) which you then use to push the data.
One important thing to note: storing files in a database is generally not good design.
You could look at how Sharepoint solves this. They have created a web interface for documents stored in their database.
Its not that hard to create or embed a webserver in your application that can serve pages to Word. You don't even have to use the standard ports.
There probably isn't any straight-forward way of doing this. I found a couple of solutions searching for it:
Use the OpenOffice SDK to manipulate the document instead of Word
Interop
Write the data to the clipboard, and then from the Clipboard to Word
I don't know if this does it for you, but apparently the API doesn't provide what you're after (unfortunately).
There are really only 2 ways to open a Word document programmatically - as a physical file or as a stream. There's a "package", but that's not really applicable.
The stream method is covered here: https://learn.microsoft.com/en-us/office/open-xml/how-to-open-a-word-processing-document-from-a-stream
But even it relies on there being a physical file in order to form the stream:
string strDoc = #"C:\Users\Public\Public Documents\Word13.docx";
Stream stream = File.Open(strDoc, FileMode.Open);
The best solution I can offer would be to write the file out to a temp location where the service account for the application has permission to write:
string newDocument = #"C:\temp\test.docx";
WriteFile(byteArray, newDocument);
If it didn't have permissions on the "temp" folder in my example, you would simply just add the service account of your application (application pool, if it's a website) to have Full Control of the folder.
You'd use this WriteFile() function:
/// <summary>
/// Write a byte[] to a new file at the location where you choose
/// </summary>
/// <param name="byteArray">byte[] that consists of file data</param>
/// <param name="newDocument">Path to where the new document will be written</param>
public static void WriteFile(byte[] byteArray, string newDocument)
{
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
// Save the file with the new name
File.WriteAllBytes(newDocument, stream.ToArray());
}
}
From there, you can open it with OpenXML and edit the file. There's no way to open a Word document in byte[] form directly into an instance of Word - Interop, OpenXML, or otherwise - because you need a documentPath, or the stream method mentioned earlier that relies on there being a physical file. You can edit the bytes you would get by reading the bytes into a string, and XML afterwards, or just edit the string, directly:
string docText = null;
byte[] byteArray = null;
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(documentPath, true))
{
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd(); // <-- converts byte[] stream to string
}
// Play with the XML
XmlDocument xml = new XmlDocument();
xml.LoadXml(docText); // the string contains the XML of the Word document
XmlNodeList nodes = xml.GetElementsByTagName("w:body");
XmlNode chiefBodyNode = nodes[0];
// add paragraphs with AppendChild...
// remove a node by getting a ChildNode and removing it, like this...
XmlNode firstParagraph = chiefBodyNode.ChildNodes[2];
chiefBodyNode.RemoveChild(firstParagraph);
// Or play with the string form
docText = docText.Replace("John","Joe");
// If you manipulated the XML, write it back to the string
//docText = xml.OuterXml; // comment out the line above if XML edits are all you want to do, and uncomment out this line
// Save the file - yes, back to the file system - required
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(docText);
}
}
// Read it back in as bytes
byteArray = File.ReadAllBytes(documentPath); // new bytes, ready for DB saving
Reference:
https://learn.microsoft.com/en-us/office/open-xml/how-to-search-and-replace-text-in-a-document-part
I know it's not ideal, but I have searched and not found a way to edit the byte[] directly without a conversion that involves writing out the file, opening it in Word for the edits, then essentially re-uploading it to recover the new bytes. Doing byte[] byteArray = Encoding.UTF8.GetBytes(docText); prior to re-reading the file will corrupt them, as would any other Encoding I tried (UTF7,Default,Unicode, ASCII), as I found when I tried to write them back out using my WriteFile() function, above, in that last line. When not encoded and simply collected using File.ReadAllBytes(), and then writing the bytes back out using WriteFile(), it worked fine.
Update:
It might be possible to manipulate the bytes like this:
//byte[] byteArray = File.ReadAllBytes("Test.docx"); // you might be able to assign your bytes here, instead of from a file?
byte[] byteArray = GetByteArrayFromDatabase(fileId); // function you have for getting the document from the database
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Open(mem, true))
{
// do your updates -- see string or XML edits, above
// Once done, you may need to save the changes....
//wordDoc.MainDocumentPart.Document.Save();
}
// But you will still need to save it to the file system here....
// You would update "documentPath" to a new name first...
string documentPath = #"C:\temp\newDoc.docx";
using (FileStream fileStream = new FileStream(documentPath,
System.IO.FileMode.CreateNew))
{
mem.WriteTo(fileStream);
}
}
// And then read the bytes back in, to save it to the database
byteArray = File.ReadAllBytes(documentPath); // new bytes, ready for DB saving
Reference:
https://learn.microsoft.com/en-us/previous-versions/office/office-12//ee945362(v=office.12)
But note that even this method will require saving the document, then reading it back in, in order to save it to bytes for the database. It will also fail if the document is in .doc format instead of .docx on that line where the document is being opened.
Instead of that last section for saving the file to the file system, you could just take the memory stream and save that back into bytes once you are outside of the WordprocessingDocument.Open() block, but still inside the using (MemoryStream mem = new MemoryStream() { ... } statement:
// Convert
byteArray = mem.ToArray();
This will have your Word document byte[].

Categories