Design - Log user downloads - c#

I have to log the download requests for each file that is grabbed off of a website. This log has to have the Employee ID, Solution ID, IP Address. I've used quite a few methods -
First, I was using a model where I was putting the path of the file
in an anchor tag . Whenever the user clicked on this anchor tag, I
was generating an AJAX request to log the file download.
But the huge drawback of this is that the user can just copy the file and paste it in a seperate window to get the file. That would ensure that the download was not logged.
Second,
When I was processing the ajax request in the web method in a page. I tried transmitting the file through HttpResponse, but that didn't work either.
HttpContext.Current.Response.TransmitFile("filename");
jQuery ajax call kept failing, and I never got the file on the client side.
The key thing is, I have to do the whole thing without refreshing the page.
I'm wondering if this is possible at all...

You could implement a IHttpHandler that logs the request, retrieves the file and serves it. This way, even if the link is copied and pasted directly, it would still log it.
public class SimpleHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
string fileToServe = context.Request.QueryString["file"];
if (!string.IsNullOrEmpty(fileToServe))
{
//Log request here...
context.Response.ContentType = "content type for your file here";
context.Response.WriteFile("~/path/" + fileToServe);
}
}
}

You could use the AJAX method, with an identifier in the link to be used as a parameter value to refer to the file - rather than storing the full path - and have your web method return the serialized data of the file.
So, your web method might look something like this:
[WebMethod]
public static string GetDownload(string someIdentifier) {
// get the contents of your file, then...
// do your logging, and...
var serializer = new JavaScriptSerializer();
return serializer.Serialize(fileByteArrayOrSuch);
}
Then handle the file contents on the client side. There will doubtless be some more trifling elements to add to your function for the sake of logging; but the bottom line is, your AJAX can both handle logging and then process the download request.

This is very much possible – you need to return a file as a response to an action (Mvc) or to an aspx page (webforms). So when the action or aspx page is hit you can log the request and write the file to the response.
Edit: for webforms example, see this SO question
For mvc:
public ActionResult DownloadFile(string fileHint)
{
// log what you want here.
string filePath = "determine the path of the file to download maybe using fileHint";
return File(filePath, "application/octet-stream"); // second argument represents file type; in this case, it's a binary file.
}

Related

Return Data and File from MVC Action

I have a MVC GET action that returns a json class model with a byte[] property, I use this to return a message and the file, but if the file is too big, I would get a OutOfMemoryException, is there any other way to return file and data that wouldn't use such memory?
I know I can create another action to return only the final file, but can I do it on the same request?
Edit:
I don't want to use byte[] or any method that will load the file into memory
public ActionResult GetFile()
{
// Here: currently logic to create the final file and the message
// Here: I want to add the final file to the result
// Here: I want to add my model data(json) to the result
// Here: currently returning json with a byte[] property to return the file
return Json(myModel, JsonRequestBehavior.AllowGet);
}
Depending on your restrictions you can do something tricky, if you are able to upload a file on the host, you can return the URL pf uploaded file in your JSON result and then let the user to download it.
In addition, you can find this link helpful https://stackoverflow.com/a/51526234/7855321.

how to check url is file type or webpage type in c#?

I have written in C# application to crawl websites.
Now I have a problem
I can identify that this URL leads to a file or a webpage!
How can I solve this problem without having to send the requested URL?
You can't without sending a request... As Uniform Resource Locator is not comparable to a File System Path. For instance, while the following url ends with a .jpg, it is clearly not a picture :
google.com/search?q=asd.jpg
Here is how, if you decided to change mind :
public bool IsFileContent(string url)
{
var request = HttpWebRequest.Create(url);
request.Method = "HEAD";
switch (request.GetResponse().ContentType)
{
case "image/jpeg": return true;
case "text/plain": return true;
case "text/html": return false;
default: // TODO: add more case as needed
throw new ArgumentOutOfRangeException();
}
}
What you are asking to do is literally impossible. URLs do not 'lead to files or web pages.' They are routed to request handlers. A request handler can return an HTML response or a file download or other types of responses. Some extensions such as ".html" or ".pdf" imply what the type of response should be. But a URL could have an extension that doesn't indicate the response type, or (as on this very page) no extension at all.
You cannot determine the response type of an HTTP request from the URL alone.
Without sending any request the only thing I could think of is to check for a file extention at the end of url. This won't give you a 100% success rate, because you can send a file using a url that doesn't end on a extension. That being said it is common practice to let a file url end on the filename with the extension

File download via Webapi controller - handling errors

I am working on an application that provides some links for users to download files.
The page itself is served up by an MVC controller but the links are pointing to a WebAPI controller running on a separate domain.
(I would have preferred same domain but for various reasons it has to be a separate project and it will run on a separate domain. I don't think CORS is part of the issue anyway as this is not using XHR, but I mention it just in case).
So in development, the main MVC project is http://localhost:56626/Reports/
And the links on the page might look like this:
Report 12345
where port 51288 is hosting the Web API.
The WebAPI controller uses ReportID to locate a file, and write its contents into the response stream, setting the disposition as an attachment:
//security.permission checks and scaffolding/database interaction
//left out for clarity
try
{
string filename = #"C:\ReportFiles\TestReport.csv";
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
var disp = new ContentDispositionHeaderValue("attachment");
disp.FileName = "TestReport.csv";
result.Content.Headers.ContentDisposition = disp;
return result;
}
catch(Exception ex)
{
//how to return a response that won't redirect on error?
}
By doing this the user can then click on the link and without any redirection, the user gets prompted to save or open the file, which is what I want; they stay on the original page with the links and just get an Open/Save dialog from the browser.
The problem arises when something goes wrong in the Web API controller - either an exception or some internal logic condition that means the file cannot be downloaded.
In this case when clicking the link, the download doesn't happen (obviously) and they get taken to the target URL instead i.e http://localhost:51288/api/ReportDownload?ReportID=12345 which is not desirable for my requirements.
I would much rather be able to catch the error somehow on the client-side by returning for e.g. HTTP 500 in the response, and just display a message to the user that the download failed.
Now to be honest, I don't even understand how the browser can do the "in place" File/Save dialog in the first place:
I always thought if you click a link that has no explicit target attribute,the browser would just open the new request in your current tab i.e it's just another GET request to the target URL, but it seems this is not the case
The browser seems to be doing a hidden background fetch of the target URL in this case (same behaviour in FF,Chrome and IE) which I cannot even see in the F12 tools.
The F12 Network log shows no activity at all except in the specific case where the response has NOT been setup as Content-Disposition: attachment i.e an error -only in this case do I see the (failed) HTTP GET being logged in the Network request list.
I suppose I could just catch any exception in the controller and send back a dummy file called "Error.csv" with contents "Ha Ha Nope!" or something similar, but that would be a last resort...any ideas welcome!
If the user clicks on the link, the browser will follow it - then depending on the response headers and browser configuration, it'll either show the file dialog or render directly - you can't really change that behavior (apart from using preventDefault when the link is clicked, which kind of defeats the purpose).
I'd suggest taking a closer look at http://jqueryfiledownload.apphb.com/ which lets you do something like this:
$.fileDownload('some/file/url')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
Then you could bind the download action using jQuery.

Using .ajaxForm and C# MVC, how can I prevent Internet Explorer from trying to save Json result?

I'm returning some Json via a C# MVC Controller. Other browsers work fine, but Internet Explorer (IE9) tries to Save the returned Json data as a Text file. Any ideas how to keep this from happening?
//MVC Controller Code...
return Json(new { redirectToUrl = Url.Action("Create", "Album",
new { url = url, isLocalFile = isLocalFile})});
//Clientside Javascript Code
$("#uploadImageForm").ajaxForm({
beforeSubmit: function () {
},
success: function (data, textStatus, xhr) {
window.location.href = data.redirectToUrl;
},
error: function (data, textStatus, xhr) {
}
});
I've tried adding "text/plain" and "text/json" to the second argument of the return Json method, it doesn't work.
Many thanks!
Quote from the documentation of the jquery.form plugin:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to.
This means that if your form contains file input fields and you are submitting this form to a controller action that returns JSON, you must wrap this JSON in a <textarea> tags.
So your response should not look like this:
{ "redirectToUrl":"some url" }
it should look like this:
<textarea>{ "redirectToUrl":"some url" }</textarea>
In order to achieve that you could use a custom action result that will wrap the response with those tags:
public class TextareaJsonResult : JsonResult
{
public TextareaJsonResult(object data)
{
this.Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
bool shouldWrap = !context.HttpContext.Request.IsAjaxRequest();
if (shouldWrap)
{
response.Write("<textarea>");
}
base.ExecuteResult(context);
if (shouldWrap)
{
response.ContentType = "text/html";
response.Write("</textarea>");
}
}
}
and then have your controller action return this custom result:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
// ... some processing
var redirectToUrl = Url.Action(
"Create",
"Album",
new { url = url, isLocalFile = isLocalFile }
);
return new TextareaJsonResult(new { redirectToUrl = redirectToUrl });
}
Now obviously in your AJAX success callback you also need to account for this difference by testing the typeof result and in the case of a legacy browser (such as Internet Explorer) manually parse the response. You may take a look at the source code of the page I have linked to.
But this being said, I can see that in your success callback you are redirecting to a controller action contained in the JSON response returned by the server. Here comes my question: What's the point of using AJAX in the first place if you are going to redirect? Why don't you use a standard form post to the controller action and have the controller action directly perform the redirect? AJAX should be used when you want to stay on the same page.
I agree with Jesse's comment, this is probably a duplicate of the link he provided.
As such I'll provide an alternative. I prefer to view json coming over the wire using a http proxy similar to fiddler http://www.fiddler2.com/fiddler2/ . I mention fiddler because it works with all browsers. The advantage is that you get a treeview of the parsed json. It's MUCH easier to read and find what you're looking for.
Also, I believe firebug for firefox, chrome dev tools, and ie dev tool all provide the same functionality. (I know chrome has the treeview, I think I remember firebug having it, and I would be surprised if modern IE doesn't have it considering the other 2 do.)

To upload any content on server...what to use?

i am making one application,
what to use if i want to upload data on server in C# based application.
i know this names in web services [ i don't know How to use it? ]
1. SOAP,
2. REST,
3. AWS
So my question is,
How many ways i can upload my data file to server?
Do i have to use web service or is their any other way to upload data file?
btw... i am just beginner in C# and web service...so may be u will find this question simple.
thanks in advance,
nitz.
EDIT :
my app. is on windows based.....
and the files which will be generated from my app. , that i want to store at server.....
You don't need to use any fancy "web service" technology to upload data from a client application to a server. You can create a simple ASP.NET Web handler and save the input file from the request body:
public class FileUploader : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string fileName = context.Request.QueryString["FileName"];
byte[] fileData = context.Request.BinaryRead(context.Request.ContentLength);
string path = ...; // decide where you want to save the file
File.WriteAllBytes(path, fileData);
}
public bool IsReusable
{
get { return false; }
}
}
From the client app, you'd use WebClient.UploadFile method:
using (var client = new WebClient()) {
client.QueryString["FileName"] = fileName;
client.UploadData(url, inputFileData);
}
This approach eliminates the overhead of protocols like SOAP and posts the file directly in the HTTP request body.
If you really want to up-/download **files* you should maybe take the File Transfer Protocol (FTP).
FtpWebRequest

Categories