Add dynamic FileName to DotNetZip MVC Extension Method - c#

I'm using the DotNetZip MVC Extension Method example for adding multiple files (I am getting mine from a repository) but I can't seem to figure out how to pass my own fileName into the extension method and get a result other than "file.zip", which is their examples hardcoded default value. Below is my CSHTML code, my Action and my Extension Method. You will see in my Action that I have a filename I want to use.
I'm embarrassed to show my attempts, but you can see what I'd like to use for my filename. Any suggestions?
CSHTML (Razor)
Download
Controller Action:
public ActionResult Download(int id)
{
var allImages = _repo.GetImagesByRender(id);
var list = new List<String>();
var render = _repo.GetRenderById(id);
var fileName = render.Select(r => r.Title);
foreach (var img in allImages)
{
list.Add(Server.MapPath("~/ImageStore/" + img.Path));
}
return new ZipResult(list);
}
The Extension Method
public class ZipResult : ActionResult
{
private IEnumerable<string> _files;
private string _fileName;
public string FileName
{
get
{
return _fileName ?? "file.zip";
}
set { _fileName = value; }
}
public ZipResult(params string[] files)
{
this._files = files;
}
public ZipResult(IEnumerable<string> files)
{
this._files = files;
}
public override void ExecuteResult(ControllerContext context)
{ // using clause guarantees that the Dispose() method is called implicitly!
using (ZipFile zf = new ZipFile())
{
zf.AddFiles(_files, false, "");
context.HttpContext.Response
.ContentType = "application/zip";
context.HttpContext.Response
.AppendHeader("content-disposition", "attachment; filename=" + FileName);
zf.Save(context.HttpContext.Response.OutputStream);
}
}
}
As for the Repo, it returns the proper Images collection associated by RenderId and also the propper Render so that I can use the Render Title as the fileName, but how would I modify the ACtion and the Extended Action Method in order to make my zipFile have the proper name?

You can add another constructor to your ZipResult class:
...
public ZipResult(IEnumerable<string> files, string fileName)
{
this._files = files;
this._fileName = fileName;
}
...
Then in controller you cne use it:
...
return new ZipResult(list, "test.zip");

Related

In C# async and await the global variable crashed

The C# app searches for a matching string in a folder with various excel files, each file with various rows and there might be various match strings found in each file. The result will be kept in a global variable. it worked perfectly
without async task but when I tried async/await, the global variable receive some wrong result. can I know how to solve this problem.
public IList<SearchList> SearchResult { get; private set; }
public async Task ReadExcelData(string stringSearch)
{
string filePath = Path.Combine("D:/test/", "Packing List Folder");
SearchResult = new List<SearchList>();
string[] fileArray = Directory.GetFiles(filePath, "*.xls*", SearchOption.AllDirectories);
List<Task> tasks = new List<Task>();
foreach (var file in fileArray)
{
tasks.Add(Task.Run(() => GetXlData(file, stringSearch)));
}
await Task.WhenAll(tasks);
}
private void GetXlData(string file, string stringSearch)
{
using (ExcelEngine excelEngine = new ExcelEngine())
{
// open the file and read every cells here.
if (str == stringSearch)
{
PList =sheet[row, col].Value2;
unit =sheet[row, col+2].Value2;
WriteData(PList, unit);
}
workbook.Close();
stream.Close();
}//how to made a return SearchResult in each task??
private void WriteData(string cde, string unt)
{
SearchResult.Add(new SearchList
{
Code = cde,
Unit = unt,
});
}
public class SearchList
{
public string Code { get; set; }
public string Unit { get; set; }
public override string ToString()
{
return Code;
}
}
this is the app structure I made..
First option is to use a lock
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement
so you set the lock, access the global variable and release the lock again. So only one thread at a time can write to the variable. But I would not do this. Don't use global variables when dealing with threads.
Second option:
A task can return something.
after the Task.WhenAll you can iterate over your tasks and get the results (Result property in the task) and add them to your global variable.
I rewrote code to return SearchList from GetXlData as plan but it show error CS0029 Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Threading.Tasks.Task', at
tasks.Add(Task.Run(() => GetXlData(file, stringSearch)));
Can help me solve this error. Thanks.
public IList<SearchList> SearchResult { get; private set; }
public async Task ReadExcelData(string stringSearch)
{
string filePath = Path.Combine("D:/test/", "Packing List Folder");
SearchResult = new List<SearchList>();
string[] fileArray = Directory.GetFiles(filePath, "*.xls*", SearchOption.AllDirectories);
List<Task<SearchList>> tasks = new List<Task<SearchList>>();
foreach (var file in fileArray)
{
tasks.Add(Task.Run(() => GetXlData(file, stringSearch)));
}
var results = await Task.WhenAll(tasks);
foreach (var item in results)
{
SearchResult.Add(item);
}
}
private List<SearchList> GetXlData(string file, string stringSearch)
{
//i guess there is error here but unable fix this.
List<SearchList> container = new List<SearchList>();
using (ExcelEngine excelEngine = new ExcelEngine())
{
// open the file and read every cells here.
if (str == stringSearch)
{
WriteData(PList, unit);
}
container.AddRange((IEnumerable<SearchList>)WriteData(PList, unit));
}
return container;
}
private SearchList WriteData(string cde, string unt)
{
SearchList output = new SearchList
{
Code = cde,
Unit = unt
};
return output;
}
grateful everyone support me..Thanks.

CefSharp custom SchemeHandler

Iam using CefSharp's SchemeHandler in order to grab resources from my C# project like .css, .js or .png files using a custom url for example custom://cefsharp/assets/css/style.css
I've 2 custom classes in order to archive this.
First class, MyCustomSchemeHandlerFactory will be the one that handles the custom Scheme and it looks like this, where "custom" will be the custom scheme:
internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new MyCustomSchemeHandler();
}
}
The next class I've implemented is MyCustomSchemeHandler which will receive the call and output a response and it looks like this:
internal class MyCustomSchemeHandler : IResourceHandler
{
private static readonly IDictionary<string, string> ResourceDictionary;
private string mimeType;
private MemoryStream stream;
static MyCustomSchemeHandler()
{
ResourceDictionary = new Dictionary<string, string>
{
{ "/home.html", Properties.Resources.index},
{ "/assets/css/style.css", Properties.Resources.style}
};
}
public Stream Stream { get; set; }
public int StatusCode { get; set; }
public string StatusText { get; set; }
public string MimeType { get; set; }
public NameValueCollection Headers { get; private set; }
public Stream GetResponse(IResponse response, out long responseLength, out string redirectUrl)
{
redirectUrl = null;
responseLength = -1;
response.MimeType = MimeType;
response.StatusCode = StatusCode;
response.StatusText = StatusText;
response.ResponseHeaders = Headers;
var memoryStream = Stream as MemoryStream;
if (memoryStream != null)
{
responseLength = memoryStream.Length;
}
return Stream;
}
public bool ProcessRequestAsync(IRequest request, ICallback callback)
{
// The 'host' portion is entirely ignored by this scheme handler.
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
string resource;
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
var resourceHandler = ResourceHandler.FromString(resource);
stream = (MemoryStream)resourceHandler.Stream;
var fileExtension = Path.GetExtension(fileName);
mimeType = ResourceHandler.GetMimeType(fileExtension);
callback.Continue();
return true;
}
else
{
callback.Dispose();
}
return false;
}
void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
{
responseLength = stream == null ? 0 : stream.Length;
redirectUrl = null;
response.StatusCode = (int)HttpStatusCode.OK;
response.StatusText = "OK";
response.MimeType = mimeType;
}
bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
{
//Dispose the callback as it's an unmanaged resource, we don't need it in this case
callback.Dispose();
if (stream == null)
{
bytesRead = 0;
return false;
}
//Data out represents an underlying buffer (typically 32kb in size).
var buffer = new byte[dataOut.Length];
bytesRead = stream.Read(buffer, 0, buffer.Length);
dataOut.Write(buffer, 0, buffer.Length);
return bytesRead > 0;
}
bool CanGetCookie(Cookie cookie)
{
return true;
}
bool CanSetCookie(Cookie cookie)
{
return true;
}
void Cancel()
{
}
}
Inside this class I've defined a custom resource dictionary which will dictate what file from the resources will be used, so as I stated in the first example, custom://cefsharp/assets/css/style.css should load the resource Properties.Resources.style, the problem is that nothing gets loaded once I enter to the specific url, I've tried to output the mimeType and It works but somehow the file itself won't output correctly. Is there something wrong with my implementation?
Additionaly I've tried to output the raw file in the form of:
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
MessageBox.Show(resource);
}
And it outputs the correct file without any problems.
To load the custom Scheme I use the following code before initializing CefSharp:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});
The above classes were based on the following links:
MyCustomSchemeHandlerFactory: FlashResourceHandlerFactory.cs
MyCustomSchemeHandler: CefSharpSchemeHandler.cs and ResourceHandler.cs
Since Cefsharp changed a bit in last few months here is an updated and easier way of handling 'file' protocol. I wrote blog post on this matter.
What you want to add is your scheme handler and its factory:
using System;
using System.IO;
using CefSharp;
namespace MyProject.CustomProtocol
{
public class CustomProtocolSchemeHandler : ResourceHandler
{
// Specifies where you bundled app resides.
// Basically path to your index.html
private string frontendFolderPath;
public CustomProtocolSchemeHandler()
{
frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
}
// Process request and craft response.
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
var requestedFilePath = frontendFolderPath + fileName;
if (File.Exists(requestedFilePath))
{
byte[] bytes = File.ReadAllBytes(requestedFilePath);
Stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
MimeType = GetMimeType(fileExtension);
callback.Continue();
return true;
}
callback.Dispose();
return false;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
}
And then register it before calling Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
If you simply need to return a string, then you can use ResourceHandler.FromString(html, mimeType). For this you just need to implement the ISchemeHandlerFactory.
https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp/ResourceHandler.cs#L98
Example reading from a file https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs#L17 which can be translated to reading from a string quite simply.

Creating an instance of HttpPostedFileBase for unit testing

I need to create an instance of HttpPostedFileBase class object and pass it to a method, but I cannot find any way to instantiate it. I am creating a test case to test my fileupload method.
This is my method which takes an HttpPostedFileBase object. I need to call it from my test case class. I am not using any mock library.
Is there a simple way to do this?
[HttpPost]
public JsonResult AddVariation(HttpPostedFileBase file, string name, string comment, string description, decimal amount, string accountLineTypeID)
{
var accountLineType = _fileService.GetAccountLineType(AccountLineType.Debit);
if (Guid.Parse(accountLineTypeID) == _fileService.GetAccountLineType(AccountLineType.Credit).AccountLineTypeID)
{
amount = 0 - amount;
}
var info = new File()
{
FileID = Guid.NewGuid(),
Name = name,
Description = description,
FileName = file.FileName,
BuildID = Guid.Parse(SelectedBuildID),
MimeType = file.ContentType,
CreatedUserID = CurrentUser.UserID,
UpdatedUserID = CurrentUser.UserID,
Amount = amount,
};
var cmmnt = new Comment()
{
CommentDate = DateTime.Now,
CommentText = comment,
FileID = info.FileID,
UserID = CurrentUser.UserID
};
_variationService.AddVariation(info, file.InputStream);
_variationService.AddComment(cmmnt);
return Json("Variation Added Sucessfully", JsonRequestBehavior.AllowGet);
}
HttpPostedFileBase is an abstract class so therefore it cannot be directly instantiated.
Create a class that derives from HttpPostedFileBase and returns the values you are looking for.
class MyTestPostedFileBase : HttpPostedFileBase
{
Stream stream;
string contentType;
string fileName;
public MyTestPostedFileBase(Stream stream, string contentType, string fileName)
{
this.stream = stream;
this.contentType = contentType;
this.fileName = fileName;
}
public override int ContentLength
{
get { return (int)stream.Length; }
}
public override string ContentType
{
get { return contentType; }
}
public override string FileName
{
get { return fileName; }
}
public override Stream InputStream
{
get { return stream; }
}
public override void SaveAs(string filename)
{
throw new NotImplementedException();
}
}
I think that #BenjaminPaul has the best answer - but wanted to add this in case anyone else is looking to test the content length on the MyTestPostedFileBase object.
I created the class as is outlined above and then passing a stream that is filled with random bytes - this allows the `MyTestPostedFileBase.ContentLength to return a testable value that I needed.
byte[] byteBuffer = new Byte[10];
Random rnd = new Random();
rnd.NextBytes(byteBuffer);
System.IO.MemoryStream testStream = new System.IO.MemoryStream(byteBuffer);
and then instantiate it:
var TestImageFile = new MyTestPostedFileBase(testStream, "test/content", "test-file.png");

Searching and Retriving image from a folder

hi i am doing a project in MVC4 using c#.
in my project i am storing some group of persons details in database with unique id.and also storing the profile pic of particular person in a folder in my project.(Id number and pic name is same).
in view i want display all these details.Problem is in images. I use the following codes
View
#foreach(var item in Model)
{
<td><img src="#Url.Action("ImageRetrive", "Member", new {imgname=(item.Id)})" /><br />Rtn. #item.Mem_NA<br />(#item.Mem_Occ)</td>
}
controller
public string ImageRetrive(int imgname)
{
string keyword=image.ToString();
string imagefolderpath = Server.MapPath("~/Content/Member/MemberPhotos");
string currentimage = new Member().GetImage(imagefolderpath,keyword);
string fullpath = "~/Content/Member/MemberPhotos/" + currentimage;
return fullpath;
}
model
public string GetImage(string path,string keyword)
{
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] images = di.GetFiles();
foreach (FileInfo image in images)
{
var name = image.Name;
if (name.Contains(keyword))
{
imgname = name;
}
}
return imgname;
}
But i didnot get any output. in the controller the variable fullpath is giving the link. but it can take in . Please help me.
I think you need is FilePathResult, change your controller code as
public FilePathResult ImageRetrive(int imgname)
{
string keyword=image.ToString();
string imagefolderpath = Server.MapPath("~/Content/Member/MemberPhotos");
string currentimage = new Member().GetImage(imagefolderpath,keyword);
string fullpath = "~/Content/Member/MemberPhotos/" + currentimage;
return File(fullpath, "image/png"); //Changed here
}
or You can use FileContentResult
public FileContentResult Retrive(int imgname)
{
return File(ConvertToByteArray(YourFile), "image/png"); //Changed here
}
In your view you pass imgname as parameter and in your controller you get image.
Controller
public string ImageRetrive(int imgname){ }
Change return path
return File(fullpath, "text/plain");

Cross-Platform reading of xml files

I have some Config files as part of my solution on Windows Mobile. I am porting the code to MonoForAndroid and MonoTouch, so I want to keep my code unchanged as much as possible.
When loading these xml files, on Windows Mobile works fine, in my last prototype it also worked on iOS, but the code does not work on MonForAndroid
I have these files
/solution folder
/My Documents/
/Business
App.Config
Settings.Config
I have these files build action set to Content and I can see that they are being copied to the /bin/Debug/ but When I try to read these files, I get the following exception:
System.IO.DirectoryNotFoundException
I see that there is a similar question in here, but they advised to use AndroidResources, which I do not want to do, there are many placed where these files are needed, so I do not want to change it in many places.
AndrodiResources, is out of the question, and if possible I would like to avoid using EmbededResources
ah and the way I am reading it, very straightforward xmDoc.Load(filePath) I also tried File.ReadAllText() I made sure that the filePath is correct, and I got the path generated using Path.Combine() to avoid any issues with the filePath/slashes
Here is how I construct my file path
var filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, ""), "My Documents", "Business");
filePath = Path.Combine(filePath, "App.Config");
And I can see in the debugger that the filePath is correct
Thanks for the help in advance
After searching all around, I could not get the MonoDroid to load (or include) my files when their build action is set to Content.
I had to create an entity called FileHelper which is implemented differently on Android, I then use that FileHelper.ReadAllText(string filename);
I will put my implementation here, hoping that it would benefit somebody else.
Windows Mobile and iOS
public class FileHelper
{
public static string ReadAllText(string filePath)
{
var path = filePath.GetFullPath();
if (!File.Exists(path))
{
Logging.LogHandler.LogError("File " + path + " does not exists");
return string.Empty;
}
using (var reader = new StreamReader(filePath))
{
return reader.ReadToEnd();
}
}
}
Android version
public class FileHelper : BaseFileHelper
{
public static string ReadAllText(string filePath)
{
var entryAssemblyPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:", ""), "MyExecutableAssemblyName.dll");
// This is because Assembly.GetEntryAssembly() returns null on Android... Booohhh
var assembly = Assembly.LoadFrom(entryAssemblyPath);
using (var stream = assembly.GetManifestResourceStream(filePath.GetFullPath()))
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
I had a shared code for Constants and an extention method for paths as below
Constants.cs
public static Class Constants
{
private static string _RootPath;
private static string _iOSRootPath;
private static string _AndroidResourcePath;
public static string RootPath
{
get
{
if (string.IsNullOrEmpty(_RootPath))
{
_RootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "") + "\\My Documents\\Business";
}
return _RootPath;
}
}
public static string iOSRootPath
{
get
{
if (!string.IsNullOrEmpty(_iOSRootPath))
{
_iOSRootPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "").Replace("file:", ""), Path.Combine("My_Documents", "Business"));
}
return _iOSRootPath;
}
}
public static string AndroidResourcePath
{
get
{
if (string.IsNullOrEmpty(_AndroidResourcePath))
{
_AndroidResourcePath = "Leopard.Delivery.My_Documents.Business.";
}
return _AndroidResourcePath;
}
}
}
PathExtentions.cs
public static class PathExtensions
{
public static string GetFullPath(this string filePath)
{
if (Platform.IsAndroid) // platform is a class that I have to tell me which platfrom I am at :)
{
return Constants.AndroidResourcePath + filePath;
}
if (Platform.IsIOS)
{
return Path.Combine(Constants.iOSRootPath, filePath);
}
return Path.Combine(Constants.RootPath, filePath);
}
}
After setting this up, I am using my FileHelper just as easy as below
string configuratinContents = FileHelper.ReadAllText(configruationPath);
To whoever using this code, remember to set the build action to EmbededResources on Android, and to Content on iOS and Windows Mobile.

Categories

Resources