Can't return word document, generated in memory stream, for downloading to user - asp.net

I've got a problem while resolving one task. The task was: Create an opportunity for users to download a .docx document with pasted data. But I'm stuck at the moment at sending the file as a byte array (taken from a MemoryStream) to the context's response. Here's a sample:
using (MemoryStream stream = new MemoryStream(FileBytes))
{
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
foreach (SdtElement obj in mainPart.Document.Body.Descendants<SdtElement>().ToList())
{
foreach (Text t in obj.Descendants<Text>().ToList())
{
switch (t.Text)
{
//.... here is code that fiiling content control's
}
}
}
myDoc.MainDocumentPart.Document.Save();
myDoc.Close();
}
context.Response.Clear();
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.AddHeader("content-disposition", "attachment; filename=\"" + DocName + ".docx\"");
context.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
context.Response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1");
stream.Seek(0, SeekOrigin.Begin);
context.Response.BinaryWrite(stream.ToArray());
}
context.Response.Flush();
The HTTP handler doesn't return any errors - page refresh and download don't start. The handler is calling from JS. Here is the sample of the JS function:
function save_word_doc(id_btn) {
// ... here is code that gets params from default page data
jQuery.post("DataHandler.ashx?CN=" + vCN + "&CommandName=SaveWord&auctionID=" + oFormRecord.auctionID
+ "&user_login=" + user_login
+ ....
+ "&amount=" + oFormRecord.value.amount
+ "&percent=" + percent);
}
By the way, this sample code is working well in an ASP.Net MVC project, where the handler is calling from a link button. But this must work in a Web Forms project.
Update to pretend questions and incorrect answers: the file is generated and saved correctly to the local machine.

I see a few issues in your code. Firstly, new MemoryStream(FileBytes) creates a non-resizable MemoryStream, which is not what you want in case you are changing the WordprocessingDocument. You would should use new MemoryStream() to create a resizable MemoryStream and copy your FileBytes to that MemoryStream.
Secondly, since you are in a using statement, you don't need the following two lines of code. This is done automatically for you.
myDoc.MainDocumentPart.Document.Save();
myDoc.Close();
Lastly, I am not sure about your ContentEncoding value for the binary data you are sending. You might want to use a tool like Fiddler or Postman to verify what happens.

Related

Cannot upload large (>50MB) files to SharePoint 2010 document library

I'm trying to upload a large file to a document library, but it fails after just a few seconds. The upload single document fails silently, upload multiple just shows a failed message. I've turned up the file size limit on the web application to 500MB, and the IIS request length to the same (from this blog), and increased the IIS timeout for good measure. Are there any other size caps that I've missed?
Update I've tried a few files of various sizes, anything 50MB or over fails, so I assume something somewhere is still set to the webapp default.
Update 2 Just tried uploading using the following powershell:
$web = Get-SPWeb http://{site address}
$folder = $web.GetFolder("Site Documents")
$file = Get-Item "C:\mydoc.txt" // ~ 150MB
$folder.Files.Add("SiteDocuments/mydoc.txt", $file.OpenRead(), $false)
and get this exception:
Exception calling "Add" with "3" argument(s): "<nativehr>0x80070003</nativehr><nativestack></nativestack>There is no file with URL 'http://{site address}/SiteDocuments/mydoc.txt' in this Web."
which strikes me as odd as of course the file wouldn't exist until it's been uploaded? N.B. while the document library has the name Site Documents, it has the URL SiteDocuments. Not sure why...
Are you sure you updated the right webapp? Is the filetype blocked by the server? Is there adequate space in your content database? I would check ULS logs after that and see if there is another error since it seems you hit the 3 spots you would need too update.
for uploading a large file, you can use the PUT method instead of using the other ways to upload a document.
by using a put method you will save the file into content database directly. see the example below
Note: the disadvantage of the code below is you cannot catch the object that is responsible for uploading directly, on other word, you cannot update the additional custom properties of the uploaded document directly.
public static bool UploadFileToDocumentLibrary(string sourceFilePath, string targetDocumentLibraryPath)
{
//Flag to indicate whether file was uploaded successfuly or not
bool isUploaded = true;
try
{
// Create a PUT Web request to upload the file.
WebRequest request = WebRequest.Create(targetDocumentLibraryPath);
//Set credentials of the current security context
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = “PUT”;
// Create buffer to transfer file
byte[] fileBuffer = new byte[1024];
// Write the contents of the local file to the request stream.
using (Stream stream = request.GetRequestStream())
{
//Load the content from local file to stream
using (FileStream fsWorkbook = File.Open(sourceFilePath, FileMode.Open, FileAccess.Read))
{
//Get the start point
int startBuffer = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length);
for (int i = startBuffer; i > 0; i = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length))
{
stream.Write(fileBuffer, 0, i);
}
}
}
// Perform the PUT request
WebResponse response = request.GetResponse();
//Close response
response.Close();
}
catch (Exception ex)
{
//Set the flag to indiacte failure in uploading
isUploaded = false;
}
//Return the final upload status
return isUploaded;
}
and here are an example of calling this method
UploadFileToDocumentLibrary(#”C:\test.txt”, #”http://home-vs/Shared Documents/textfile.pdf”);

how to make a picture file downloadable?

I have an ASP.NET MVC3 application and I want to link_to an image file (png, jpeg, gif, etc), and when user clicks on it, the file goes to download, instead of the browser shows it; is there any way to do this?
take your link something like this:
#Html.ActionLink(
"Download Image", // text to show
"Download", // action name
["DownloadManager", // if need, controller]
new { filename = "my-image", fileext = "jpeg" } // file-name and extension
)
and action-method is here:
public FilePathResult Download(string filename, string fileext) {
var basePath = Server.MapPath("~/Contents/Images/");
var fullPath = System.IO.Path.Combine(
basePath, string.Concat(filename.Trim(), '.', fileext.Trim()));
var contentType = GetContentType(fileext);
// The file name to use in the file-download dialog box that is displayed in the browser.
var downloadName = "one-name-for-client-file." + fileext;
return File(fullPath, contentType, downloadName);
}
private string GetContentType(string fileext) {
switch (fileext) {
case "jpg":
case "jpe":
case "jpeg": return "image/jpeg";
case "png": return "image/x-png";
case "gif": return "image/gif";
default: throw new NotSupportedException();
}
}
UPDATE:
in fact, when a file is sending to a browser, this key/value will be generated in http-header:
Content-Disposition: attachment; filename=file-client-name.ext
which file-client-name.ext is the name.extension that you want the file save-as it on client system; for example, if you want to do this in ASP.NET (none mvc), you can create a HttpHandler, write the file-stream to Response, and just add the above key/value to the http-header:
Response.Headers.Add("Content-Disposition", "attachment; filename=" + "file-client-name.ext");
just this, enjoy :D
Well technically your browser is downloading it.
I don't think you can directly link to an image, and have the browser prompt to download.
You could try something where instead of linking directly to the image, you link to a page, which serves up the image in a zip file perhaps - which of course would prompt the download to occur.
Yes, you can.
Now, you'll need to customize this to suit your needs, but I created a FileController that returned files by an identifier (you can easily return by name).
public class FileController : Controller
{
public ActionResult Download(string name)
{
// check the existence of the filename, and load it in to memory
byte[] data = SomeFunctionToReadTheFile(name);
FileContentResult result = new FileContentResult(data, "image/jpg"); // or whatever it is
return result;
}
}
Now, how you read that file or where you get it from is up to you. I then created a route like this:
routes.MapRoute(null, "files/{name}", new { controller = "File", action = "Download"});
My database has a map of identifiers to files (it's actually more complex than this, but I am omitting that logic for brevity), I can write urls like:
"~/files/somefile"
And the relevant file is downloaded.
I don't think this is possible but a simple message saying right click to save image would suffice I think.

Calling a Remote Java Servlet

I have a jsp page which holds a form, it is supposed to send off the form data to a remote servlet, which calculates it, and then returns it as XML. It works, but at the moment I'm creating an instance and dispatcher which only works with local servlets whereas I want it to work with a remote servlet.
I was previously told that HTTPClient would do this, but this thing has become such a headache and it seems like a complete overkill for what I want to do. There must be some simple method as opposed to faffing around with all these jar components and dependencies?
Please give sample code if possible, I'm really a complete novice to Java, much more of a PHP guy :P
Figured it out with the help of some online resources. Had to first collect the submitted values (request.getParamater("bla")), build the data string (URLEnconder), start up a URLConnection and tell it to open a connection with the designated URL, startup an OutputStreamWriter and then tell it to add the string of data (URLEncoder), then finally read the data and print it...
Below is the gist of the code:
String postedVariable1 = request.getParameter("postedVariable1");
String postedVariable2 = request.getParameter("postedVariable2");
//Construct data here... build the string like you would with a GET URL
String data = URLEncoder.encode("postedVariable1", "UTF-8") + "=" + URLEncoder.encode(postedVariable1, "UTF-8");
data += "&" + URLEncoder.encode("postedVariable2", "UTF-8") + "=" + URLEncoder.encode(submitMethod, "UTF-8");
try {
URL calculator = new URL("http://remoteserver/Servlet");
URLConnection calcConnection = calculator.openConnection();
calcConnection.setDoOutput(true);
OutputStreamWriter outputLine = new OutputStreamWriter(calcConnection.getOutputStream());
outputLine.write(data);
outputLine.flush();
// Get the response
BufferedReader streamReader = new BufferedReader(new InputStreamReader(calcConnection.getInputStream()));
String line;
//streamReader = holding the data... can put it through a DOM loader?
while ((line = streamReader.readLine()) != null) {
PrintWriter writer = response.getWriter();
writer.print(line);
}
outputLine.close();
streamReader.close();
} catch (MalformedURLException me) {
System.out.println("MalformedURLException: " + me);
} catch (IOException ioe) {
System.out.println("IOException: " + ioe);
}

creating my own MJPEG stream

I'm trying to create an MJPEG stream, I have a series of jpegs that I want to put together into a stream so that a user can just hit a URL and get an mjpeg stream.
I've been trying for the last few days to get this to work, and it may just not be possible. I've brought up ethereal and listened to the packets coming from an axis camera on the net somewhere, and tried to mimmick it. I originally tried using WCF, and returning a "stream" but then later found out that I would need to set the content type on that stream, so I then tried the WCF REST api, but that suffers from the same problem. so I am now just using a bare bones HTTPListener, and handling the event. I would greatly prefer to use WCF, but I'm not sure that it will allow me to return a stream with the right content type.
so here's what I have for the httpListener .
in the handler of the listener call back I put the following.
HttpListenerResponse response = context.Response;
response.ProtocolVersion = new System.Version(1, 0);
response.StatusCode = 200;
response.StatusDescription = "OK";
response.ContentType = "multipart/x-mixed-replace;boundary=" + BOUNDARY + "\r\n";
System.IO.Stream output = response.OutputStream;
Render(output);
the Render method looks like this
var writer = new StreamWriter(st);
writer.Write("--" + BOUNDARY + "\r\n");
while (true)
{
for (int i = 0; i < imageset.Length; i++)
{
var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap;
var memStream = new MemoryStream();
resource.Save(memStream,ImageFormat.Jpeg);
byte[] imgBinaryData = memStream.ToArray();
string s = Convert.ToBase64String(imgBinaryData);
writer.Write("Content-type: image/jpeg\r\n");
foreach (var s1 in imgBinaryData)
{
writer.Write((char)s1);
}
writer.Write("\n--" + BOUNDARY + "\n");
writer.Flush();
Thread.Sleep(500);
}
}
At this point I've just added a few jpeg images as properties on the dll, and am iterating over them, eventually these will be dynamic images, but for now I just want to get the thing to work.
From what I understand about the MJPEG (spec) is that the content must be set to multipart/x-mixed-replace and a boundary set. and then you just deliminate the jpegs within the stream by the boundary.
This seems like it should be simpler then I'm making it, but I'm wondering where I'm going wrong. if I load this URL up in IE or Firefox, it just hangs. if I try to make a stub html page with an img tag, whose source is the URL then I get a broken image.
Any ideas, thanks
Josh
Well, as far as I can tell, here are your issues:
The StreamWriter is not a correct choice. Use a regular stream write function is fine. Meaning, you should write data in Byte array instead of string.
You convert the Binary data of the image to String64, the browser does not known that, still thinking it is 32bit data.
Your jpeg frame format is not correct. You should also add Content-Length to the frame header so that the application that receive the stream know when to stop reading rather than having to check for the next boundary string every read. This will result in about 4-5 times faster in reading data. And there are also inconsistency in your new line character, some are "\r\n" while some others are "\n".
While loop is a infinite loop.
So, here is the solution.
Note: There might be some syntax errors but you probably get the general idea.
private byte[] CreateHeader(int length)
{
string header =
"--" + BOUDARY + "\r\n" +
"Content-Type:image/jpeg\r\n" +
"Content-Length:" + length + "\r\n" +
+ "\r\n"; // there are always 2 new line character before the actual data
// using ascii encoder is fine since there is no international character used in this string.
return ASCIIEncoding.ASCII.GetBytes(header);
}
public byte[] CreateFooter()
{
return ASCIIEncoding.ASCII.GetBytes("\r\n");
}
private void WriteFrame(Stream st, Bitmap image)
{
// prepare image data
byte[] imageData = null;
// this is to make sure memory stream is disposed after using
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// prepare header
byte[] header = CreateHeader(imageData.Length);
// prepare footer
byte[] footer = CreateFooter();
// Start writing data
st.Write(header, 0, header.Length);
st.Write(imageData, 0, imageData.Length);
st.Write(footer, 0, footer.Length);
}
private void Render(Stream st)
{
for (int i = 0; i < imageset.Length; i++)
{
var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap;
WriteFrame(st, resource);
Thread.Sleep(500);
}
}
There is also an implementation # https://net7mma.codeplex.com/SourceControl/latest the library there can transcode the Http to Rtp compatible on the fly!

Uploading an XML file, referencing an XSD, in ASP.Net

I have an XML file which is being uploaded to an ASP.Net page via the normal file upload control. When it gets up, I am attempting to validate and deserialize the XML. However, the code below is really very handy for validating an XML file which references it's XSD like this:
xsi:schemaLocation="someurl ..\localSchemaPath.xsd"
However, if I upload this XML file, only the XML file gets uploaded, so ..\localSchemaPath.xsd doesn't exist, so it can't validate.
Even if I stored the XSD locally, it still wouldn't be quite right as the XML file could be written with a schema location like:
xsi:schemaLocation="someurl ..\localSchemaPath.xsd"
or
xsi:schemaLocation="someurl localSchemaPath.xsd"
or
xsi:schemaLocation="someurl ..................\localSchemaPath.xsd"
if it so wished.
Dilemma!
(For the purposes of this question, I have pinched the code below from: Validating an XML against referenced XSD in C#)
using System.Xml;
using System.Xml.Schema;
using System.IO;
public class ValidXSD
{
public static void Main()
{
// Set the validation settings.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
// Create the XmlReader object.
XmlReader reader = XmlReader.Create("inlineSchema.xml", settings);
// Parse the file.
while (reader.Read()) ;
}
// Display any warnings or errors.
private static void ValidationCallBack(object sender, ValidationEventArgs args)
{
if (args.Severity == XmlSeverityType.Warning)
Console.WriteLine("\tWarning: Matching schema not found. No validation occurred." + args.Message);
else
Console.WriteLine("\tValidation error: " + args.Message);
}
}
Here is a chunk of code I use to validate xml with a local schema:
string errors = string.Empty;
try
{
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(string.Empty, Page.MapPath("~/xml/Schema.xsd"));
XmlDocument doc = new XmlDocument();
doc.Schemas = schemas;
doc.Load(Page.MapPath("~/xml/sampleXML.xml"));
//use this line instead of the one above for a string in memory.
//doc.InnerXml = xmlToValidate;
ValidationEventHandler validator = delegate(object send, ValidationEventArgs ve)
{
errors += "\n" + ve.Severity + ": " + ve.Message;
};
doc.Validate(validator);
}
catch (XmlException xe)
{
errors += "\n" + xe.Message;
}
catch (XmlSchemaValidationException xe)
{
errors += "\n" + xe.Message;
}
I can't quite make out whether you are attempting a generic validate-against-any-referenced-schema, or if you have a specific schema that you validate against every time, and are just not sure how to handle the references.
If it's the latter, then make the schema public on the internet, and tell people to reference it by URI.
If it's the former, then I would suggest the following:
First the user uploads an XML file.
Parse the XML file for a schema reference. Tell them "References to yourSchema.xsd were found; please upload this file below" with a new upload box.
Then, validate the file against the uploaded schema. To do this, modify the Schemas property of your settings object, instead of modifying the ValidationFlags property.

Resources