0

我正在使用 HTML5 画布元素和新的 HTML5 文件 i\o 函数在其上放置多个文件并让它们上传。它工作正常,但现在我需要生成一个新文件名,如果目标目录中没有文件(它是一个 7 位整数)或获取最后上传的文件的名称,将其转换为 int32 并为每个新文件增加一个文件上传到同一目录。这就是GetFileName(dir);进来。第一个图像总是可以正常上传,但是一旦保存第二个文件并且进程命中 ImageJob.Build(),问题就开始了,我认为这是因为一旦新文件开始写入,GetFile() 方法就会运行同时在行中的第二个文件正在检查最后写入的文件,该文件仍在写入中,这会产生冲突。我该如何解决这个问题,也许我可以以某种方式在 Request.InputStream 数据上使用 foreach 进行迭代,或者实现某种等待进程完成的进程监视?

Update:我尝试使用 TempData 来存储生成的文件名,并在 TempData 中为所有下一个文件名增加 int 值,它似乎做得更好,可以获取更多图像,但在某些时候仍然会出错。但是 TempData 不是为此,因为它在每次读取后都会被删除,再次重新分配给它并没有帮助。也许我会尝试将它存储在会话中。

该进程无法访问文件“C:\Users\Admin\Documents\Visual Studio 2010\Projects\myproj\myproj\Content\photoAlbums\59\31\9337822.jpg”,因为它正被另一个进程使用。

public PartialViewResult Upload()
{          
    string fileName = Request.Headers["filename"];
    string catid = Request.Headers["catid"];
    string pageid = Request.Headers["pageid"];
    string albumname = Request.Headers["albumname"];


    var dir = "~/Content/photoAlbums/" + catid + "/" + pageid + "/" + (albumname ?? null);

    var noex = GetFileName(dir);

    var extension = ".jpg";

    string thumbFile = noex + "_t" + extension;
    fileName = noex + extension;

    byte[] file = new byte[Request.ContentLength];
    Request.InputStream.Read(file, 0, Request.ContentLength);

    string imgdir;
    string thumbimgdir;
    string imageurl;

    if (albumname != null) 
    { 
        imgdir = Server.MapPath("~/Content/photoAlbums/" + catid + "/" + pageid + "/" + albumname + "/" + fileName);
        thumbimgdir = Server.MapPath("~/Content/photoAlbums/" + catid + "/" + pageid + "/" + albumname + "/" + thumbFile);
        imageurl = "/Content/photoAlbums/" + catid + "/" + pageid + "/" + albumname + "/" + thumbFile;
    }
    else 
    { 
        imgdir = Server.MapPath("~/Content/photoAlbums/" + catid + "/" + pageid + "/" + fileName);
        thumbimgdir = Server.MapPath("~/Content/photoAlbums/" + catid + "/" + pageid + "/" + thumbFile);
        imageurl = "/Content/photoAlbums/" + catid + "/" + pageid + "/" + thumbFile;
    }

    ImageJob b = new ImageJob(file, imgdir, new ResizeSettings("maxwidth=1024&maxheight=768&format=jpg")); b.CreateParentDirectory = true; b.Build();
    ImageJob a = new ImageJob(file, thumbimgdir, new ResizeSettings("w=100&h=100&mode=crop&format=jpg")); a.CreateParentDirectory = true; a.Build();

    ViewBag.CatID = catid;
    ViewBag.PageID = pageid;
    ViewBag.FileName = fileName;

    return PartialView("AlbumImage", imageurl);
}

public string GetFileName(string dir)
{
    var FullPath = Server.MapPath(dir);
    var dinfo = new DirectoryInfo(FullPath);

    string FileName;
    if (dinfo.Exists)
    {           
       var Filex = dinfo.EnumerateFiles().OrderBy(x => x.Name).LastOrDefault();
       FileName = Filex != null ? Path.GetFileNameWithoutExtension(Filex.Name) : null;

       if (FileName != null)
       {
           FileName = FileName.Contains("_t") ? FileName.Substring(0, FileName.Length - 2) : FileName;
           int fnum;
           Int32.TryParse(FileName, out fnum);
           FileName = (fnum + 1).ToString();

           if (fnum > 999999) { return FileName; } //Check that TryParse produced valid int
           else 
           {
              var random = new Random();
              FileName = random.Next(1000000, 9999000).ToString();
           }
       }
       else
       {
           var random = new Random();
           FileName = random.Next(1000000, 9999000).ToString();
       }
    }
    else
    {        
           var random = new Random();
           FileName = random.Next(1000000, 9999000).ToString();
    }
    return FileName;
}
4

2 回答 2

2

如果你想生成唯一的文件名,你根本不能使用 Random 类。它使用当前时间作为种子,因此两个完全并发的请求将始终产生相同的“随机”数字。

您可以使用加密随机数生成器,但您仍然必须确保 (a) 一次只有一个线程会生成它,并且 (b) 您使用了足够长的标识符来防止生日悖论

因此,我建议每个人都在上传时使用 GUID 标识符,因为它们本质上解决了上述所有问题(我相信使用操作系统级别的锁来防止重复)。

您的方法也不处理每个请求的多个文件上传,尽管这可能是故意的。您可以通过循环Request.Files并将每个HttpPostedFile实例直接传递到 ImageJob 来支持这些。

这是使用 GUID 且不会遇到并发问题的代码的简化版本。

public PartialViewResult Upload()
{          

    string albumname = Request.Headers["albumname"];
    string baseDir = "~/Content/photoAlbums/" + Request.Headers["catid"] + "/" + Request.Headers["pageid"] + "/" (albumname != null ?  albumname + "/" : "");

    byte[] file = new byte[Request.ContentLength];
    Request.InputStream.Read(file, 0, Request.ContentLength);

    ImageJob b = new ImageJob(file, baseDir + "<guid>.<ext>", new ResizeSettings("maxwidth=1024&maxheight=768&format=jpg")); b.CreateParentDirectory = true; b.Build();
    ImageJob a = new ImageJob(file, baseDir + "<guid>_t.<ext>", new ResizeSettings("w=100&h=100&mode=crop&format=jpg")); a.CreateParentDirectory = true; a.Build();

    //Want both the have the same GUID? Pull it from the previous job.
    //string ext = PathUtils.GetExtension(b.FinalPath);
    //ImageJob a = new ImageJob(file, PathUtils.RemoveExtension(a.FinalPath) + "_t." + ext, new ResizeSettings("w=100&h=100&mode=crop&format=jpg")); a.CreateParentDirectory = true; a.Build();


    ViewBag.CatID = Request.Headers["catid"];
    ViewBag.PageID = Request.Headers["pageid"];
    ViewBag.FileName = Request.Headers["filename"];

    return PartialView("AlbumImage", PathUtils.GuessVirtualPath(a.FinalPath));
}
于 2012-06-07T14:30:04.333 回答
1

如果该过程相对较快(小文件),您可以进入一个循环,检查该异常,使线程休眠几秒钟,然后重试(最多迭代次数)。一个警告是,如果上传是异步的,您可能会错过一个文件。

其他几个建议:

  • 将 GetFileName 设为私有方法,这样它就不会从 Web 中触发。
  • Filex 查询中的 OrderBy 在达到 8 位后可能无法达到您的预期(如果第一个 Random() 是一个非常高的数字,则可能)。
  • Random() 可能应该被播种以产生更好的随机性。
于 2012-06-07T13:20:20.213 回答