My Asp.net MVC app requires a file upload. In the course of the upload I'd like to manipulate the freshly uploaded file.
public ActionResult Edit(int id, FormCollection collection) {
Block block = userrep.GetBlock(id);
foreach (string tag in Request.Files) {
var file = Request.Files[tag] as HttpPostedFileBase;
if (file.ContentLength == 0)
continue;
string tempfile = Path.GetTempFileName()
file.SaveAs(tempfile);
// This doesn't seem to make any difference!!
// file.InputStream.Close();
if (FileIsSmallEnough(file)) {
// Will throw an exception!!
File.Move(tempfile, permanentfile);
} else {
GenerateResizedFile(tempfile, permanentfile);
// Will throw an exception!!
File.Delete(tempfile);
}
block.Image = permanentfile;
}
userrep.Save();
The problem with this snippet is that any attempt to manipulate the initially uploaded file generates an IOException("The process cannot access the file because it is being used by another process.") Of course I can bypass the problem by copying rather than moving uploaded file but I still can't delete it once I have a better alternative.
Any advice?
Duffy
As you have mentioned in your comments, you load an Image from file. The MSDN documentation states that the file remains locked until the image is disposed.
http://msdn.microsoft.com/en-us/library/stf701f5.aspx
To dispose your image, you can either call the Dispose method on the instance, or use the preferred mechanism of the using statement:
private bool FileIsSmallEnough()
{
using (Image i = Image.FromFile())
{
}
}
This should solve the problem.
Related
I have a JSON file which contains data for my Localization in my game. The file is stored in Application.persistentDataPath . I read in the documentation that the file is not deleted or overwritten when updating the game. Currently I launch the app and the file is there but when I add more content and send an update to save again, it only has the original content.
But is there a way to update this file when I update the game?
What if in the future I want to add more content to this JSON file, I would have to delete it and upload it again?
Any way to update and overwrite it?
private static void Request(string file)
{
var loadingRequest = UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, file));
loadingRequest.SendWebRequest();
while (!loadingRequest.isDone)
{
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
break;
}
}
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
// Some error happened.
}
else
{
File.WriteAllBytes(Path.Combine(Application.persistentDataPath, file), loadingRequest.downloadHandler.data);
}
}
// Loads key from "StreamingAssets/language_data.json" file.
public static string LoadJson(string key)
{
Request("data.json");
var jsonPath = Path.Combine(Application.persistentDataPath, jsonFileName);
using (StreamReader r = new StreamReader(jsonPath))
{
var N = JSON.Parse(r.ReadToEnd());
string result = N[GetLanguage()][key];
return result;
}
}
I think the issue is the following:
You are only creating a file named data.json NOT languages_data.json!
In Request (you pass in "data.json") you copy
Application.streamingAssetsPath, file
to
Application.persistentDataPath, file
where file = "data.json"
Then in LoadJson you are trying to load values from
Application.persistentDataPath, jsonFileName
where - as to your comments - jsonFileName = "languages_data.json"
=> It is a different path!
In the Editor/on your PC you probably still have an old languages_data.json in the persistent data from some time ago. Therefore there is no error but it is also never updated. Delete the persistent/languages_data.json on your PC and it will throw a FileNotFoundException.
To solve this make 100% sure the file names are the same everywhere. The simplest fix code wise would be to simply rather use
Request(jsonFileName);
and make sure that jsonFileName matches the name of your existing file in StreamingAssets.
I have a web interface where users can choose one of many files from local computer and upload them to a central location, in this case Azure Blob Storage. I have a check in my C# code to validate that the filename ending is .bin. The receiving method in C# takes an array of HttpPostedFileBase.
I want to allow users to choose a zipfile instead. In my C# code, I iterate through the content of the zipfile and check each filename to verify that the ending is .bin.
However, when I iterate through the zipfile, the ContentLength of the HttpPostedFileBase object becomes 0 (zero) and when I later on upload the zipfile to Azure, it is empty.
How can I make a check for filename endings without manipulating the zipfile?
I have tried to DeepCopy a single object of HttpPostedFileBase but it is not serializable.
I've tried to make a copy of the array but nothing works. It seems that everything is reference and not value. Some example of my code as follows. Yes, I tried the lines individually.
private static bool CanUploadBatchOfFiles(HttpPostedFileBase[] files)
{
var filesCopy = new HttpPostedFileBase[files.Length];
// Neither of these lines works
Array.Copy(files, 0, filesCopy, 0, files.Length);
Array.Copy(files, filesCopy, files.Length);
files.CopyTo(filesCopy, 0);
}
This is how I iterate through the zipfile
foreach (var file in filesCopy)
{
if (file.FileName.EndsWith(".zip"))
{
using (ZipArchive zipFile = new ZipArchive(file.InputStream))
{
foreach (ZipArchiveEntry entry in zipFile.Entries)
{
if (entry.Name.EndsWith(".bin"))
{
// Some code left out
}
}
}
}
}
I solved my problem. I had to do two separate things:
First, I do not do a copy of the array. Instead, for each zip file, I just copy the stream. This made the ContentLength stay at whatever length it was.
The second thing is did was to reset the position after I looked inside the zipfile. I need to do this or else the zip file that I upload to Azure Blob Storage will be empty.
private static bool CanUploadBatchOfFiles(HttpPostedFileBase[] files)
{
foreach (var file in files)
{
if (file.FileName.EndsWith(".zip"))
{
// Part one of the solution
Stream fileCopy = new MemoryStream();
file.InputStream.CopyTo(fileCopy);
using (ZipArchive zipFile = new ZipArchive(fileCopy))
{
foreach (ZipArchiveEntry entry in zipFile.Entries)
{
// Code left out
}
}
// Part two of the solution
file.InputStream.Position = 0;
}
}
return true;
}
I am attempting to delete image files from the server file system but I get a 'file in use by another process' error. I am trying to delete the image and it's associated thumbnail in one fowl swoop. I can get one to delete but the other will try and fail forever, as far as I've seen. However, if I refresh the page while it's looping through a try-catch, the operation succeeds immediately. I am accessing the files via ajax requests and API controllers (my site is mostly one page) when I load the page, but I am doing so with 'using' statements. This should close the handle after use, but it seems to be staying open. I have to get the files on the client side to be able to have the option to delete them(browser content manager), so I don't see how they are still in use. I load the thumbnail and use it's ID to get it's name from the database, and use the name to find it in the file system.
I have tried putting a try-catch in a loop and waiting for the last process to finish, but it never does, until I refresh the page.
I also tried deleting one at a time with separate ajax requests, but it seems that if I have opened a file at all, I can't delete it (until I refresh the page).
Code for deleting file (method 1):
public static void DeleteFromFileSystem(string path)
{
for (int i = 1; i <= 10000; ++i)
{
try
{
System.IO.File.Delete(path);
break;
}
catch (IOException e) when (i <= 10000)
{
Thread.Sleep(3);
}
}
}
Code for deleting file (method 2):
public static void DeleteFromFileSystem(string path)
{
using(FileStream f = System.IO.File.OpenWrite(path))
{
System.IO.File.Delete(path);
}
}
Code for getting image (works fine, but could be the problem):
public static byte[] ImageToByte(string name, bool getThumbnail)
{
if (name == null) name = "user.png"; //default profile picture
string path = getThumbnail ? "wwwroot/ImgThumb/" : "wwwroot/ImgFull/";
// this line is my best suspect
System.Drawing.Image img = System.Drawing.Image.FromFile(path + name);
byte[] b;
using (MemoryStream mStream = new MemoryStream())
{
img.Save(mStream, img.RawFormat);
b = mStream.ToArray();
mStream.Close(); // I added this as a precautionary. It didn't help.
}
return b;
}
I expect that the images get deleted, but I get the 'file X cannot be accessed because it is in use by another process' error.
SOLVED - I added img.Dispose() after the using statement.
I have a requirement where
when uploading the files to the pick-up folder, files will be uploaded
with a .tmp (or)._ (or) .filepart extensions and after successful
upload files will be renamed to the original file name.
This is required to avoid any partial pick-up of .xml files by settings on SFTP folder side.
For eg. Upload with .xml.tmp and after successful upload, rename the files to .xml
Any idea on how to achieve this in MVC, C#.
I prefer to do this in a separate folder entirely. And then do a move to the pickup folder.
Then renaming is not required.
private bool IsFileLocked()
{
try
{
FileStream fs = File.OpenWrite(FilePath);
fs.Close();
return false;
}
catch (Exception)
{
Console.WriteLine("File locked: " + FileName);
return true;
}
}
To check if the file is locked prior to attempting to send, might also work, or in conjunction.
I was talking about generating a local file first, once its completely done being written, simply use the File.Move() method, so you move the newly generated file from its "safe" folder, into the pickup folder, that the SFTP is continually looking for files in.
If it is picking up a file you are receiving, then it's just the check prior to attempting to do anything with it.
First of all, once you receive the file stream from the post, the upload is "already" successful (most likely). Therefore, the moment you have the data from the post, you should already be good to write it. The only point I can remotely see here is that, the remote process either checks .xml files constantly so let's say if the .xml file is quite large, and let's assume (which wont be the case) it takes a while for you to write the stream to the remote destination, they do not want to check just part of the xml, they need all of it. If that is the case, something like the following should work (modify it for your needs);
[HttpPost]
public ActionResult Upload()
{
if (Request.Files.Count < 1)
{
ViewBag.Result = "No files were provided";
return PartialView("Error");
}
foreach (string F in Request.Files)
{
var FInfo = Request.Files[F];
var TemporaryFileName = $"{FInfo.FileName}.tmp";
try
{
using (var FStream = new FileStream(TemporaryFileName, FileMode.Create, FileAccess.Write))
{
FInfo.InputStream.CopyTo(FStream);
}
}
catch (Exception e)
{
ViewBag.Result = e.Message;
return PartialView("Error");
}
finally
{
System.IO.File.Move(TemporaryFileName, $"{FInfo.FileName}");
}
}
ViewBag.Result = "Files have been uploaded";
return View();
}
I'm working on a site where I need to crop and resize images that people upload.
I got the code for the upload function but in my httpPost action result I want to resize the image to start off with. I also got the code for that but I can't find a way to show the image.
Here is my code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult FileUpload(HttpPostedFileBase uploadFile)
{
if (uploadFile.ContentLength > 0)
{
foreach (string fileKey in System.Web.HttpContext.Current.Request.Files.Keys)
{
HttpPostedFile file = System.Web.HttpContext.Current.Request.Files[fileKey];
if (file.ContentLength <= 0) continue; //Skip unused file controls.
ImageResizer.ImageJob i = new ImageResizer.ImageJob(file, "~/img/", new ImageResizer.ResizeSettings("width=50;height=50;format=jpg;mode=max"));
i.CreateParentDirectory = true; //Auto-create the uploads directory.
i.Build();
string relativePath = "~/img/" + Path.GetFileName(uploadFile.FileName);
string physicalPath = Server.MapPath(relativePath);
uploadFile.SaveAs(physicalPath);
return View((object)relativePath);
}
}
I want go write out the Image information from ImageResizer.ImageJob i..
Any ideas?
Thank you!
First, the code you're using allows anybody to upload an executable page and run it.
Never, under any circumstances, should you use uploadFile.FileName as part of the final path name, unless you deeply understand path and length sanitization.
Second, ImageJob needs a destination path, not a folder path. You can use variables, like "~/img/<guid>.<ext>" and access the resulting physical path from i.FinalPath. You can use ImageResizer.Util.PathUtils.GuessVirtualPath(i.FinalPath) to get the app-relative path.
As this is almost a precise duplicate of mvc3 ImageResizer, please consider using the search feature before posting.