How to update a PDF without creating a new PDF? - asp.net

I am required to replace a word in an existing PDF AcroField with another word. I am using PDFStamper of iTEXTSHARP to do the same and it is working fine. But, in doing so it is required to create a new PDF and i would like the change to be reflected in the existing PDF itself. If I am setting the destination filename same as the original filename then no change is being reflected.I am new to iTextSharp , is there anything I am doing wrong? Please help.. I am providing the piece of code I am using
private void ListFieldNames(string s)
{
try
{
string pdfTemplate = #"z:\TEMP\PDF\PassportApplicationForm_Main_English_V1.0.pdf";
string newFile = #"z:\TEMP\PDF\PassportApplicationForm_Main_English_V1.0.pdf";
PdfReader pdfReader = new PdfReader(pdfTemplate);
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
PdfReader reader = new PdfReader((string)pdfTemplate);
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(newFile, FileMode.Create, FileAccess.ReadWrite)))
{
AcroFields form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (string fieldKey in fieldKeys)
{
//Replace Address Form field with my custom data
if (fieldKey.Contains("Address"))
{
form.SetField(fieldKey, s);
}
}
stamper.FormFlattening = true;
stamper.Close();
}
}
}

As documented in my book iText in Action, you can't read a file and write to it simultaneously. Think of how Word works: you can't open a Word document and write directly to it. Word always creates a temporary file, writes the changes to it, then replaces the original file with it and then throws away the temporary file.
You can do that too:
read the original file with PdfReader,
create a temporary file for PdfStamper, and when you're done,
replace the original file with the temporary file.
Or:
read the original file into a byte[],
create PdfReader with this byte[], and
use the path to the original file for PdfStamper.
This second option is more dangerous, as you'll lose the original file if you do something that causes an exception in PdfStamper.

Related

Edit OpenXML docx template and return it as FileResult

I want to offer users of my application to download a docx file which would contain dynamic content depending on their request.
I prepared a template with header, otherwise the OpenXML creation is pain in the somewhere.
Now I am having trouble with editing it and returning it as FileResult in my ASP MVC application.
My plan is to read a file to MemoryStream and edit it as WordprocessingDocument and then returning MemoryStream as Byte[].
I have two problems here:
If I read WordprocessingDocument directly from file without memory stream, I don't see a way to transform it to the shape FileResult() wants.
If I create new WordprocessingDocument with empty MemoryStream I can simply create content and return it as File but I lack the previously prepared header with desired content.
So, how to edit a .docx template and return it as FileResult()?
This is how you go about creating a new spreadsheet document with MemoryStream and then saving it to a Byte[]
Byte[] file;
using (MemoryStream mem = new MemoryStream())
{
using (SpreadsheetDocument spreadsheetDocument =
SpreadsheetDocument.Create(mem, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.
AppendChild<Sheets>(new Sheets());
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "aStupidSheet"
};
sheets.Append(sheet);
workbookpart.Workbook.Save()
spreadsheetDocument.Close();
}
file = mem.ToArray();
}
With the help from FortyTwo I managed to achieve my goal.
So the desired "workflow" is:
copy .docx template (added headers or other things) to memory
edit content of the document
return this document as FileResult without saving the changes to template
So here is the code:
public FileResult EditDOCXBody()
{
// Prepare file path
string file = "../WordTemplates/EmptyTemplate.docx";
String fullFilePath = HttpContext.ApplicationInstance.Server.MapPath(file);
// Copy file content to MemeoryStream via byte array
MemoryStream stream = new MemoryStream();
byte[] fileBytesArray = System.IO.File.ReadAllBytes(fullFilePath);
stream.Write(fileBytesArray, 0, fileBytesArray.Length); // copy file content to MemoryStream
stream.Position = 0;
// Edit word document content
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(stream, true))
{
MainDocumentPart mainPart = wordDoc.MainDocumentPart;
Body body = mainPart.Document.Body;
// add some text to body
body.Append(new Paragraph(
new Run(
new Text("Current time is: " + DateTime.Now.ToLongTimeString()))));
// Save changes to reflect on stream object
mainPart.Document.Save();
}
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "servedFilename.docx");
}
Some important notes:
you need to write file bytes to MemoryStream manually otherwise you get Memory stream is not expandable error. More info here
you have to use mainPart.Document.Save() for changes to take effect on MemoryStream
when returning the file, you have to use .ToArray() otherwise the returned file is corrupted

how clear text file with asp.net?

I use this code to write into a text file in my code behinde:
string filePath = HttpContext.Current.Server.MapPath("s.txt");
if (File.Exists(filePath))
{
StreamWriter writer = new StreamWriter(filePath, true);
string logMessage = "ok";
writer.WriteLine(logMessage);
writer.Flush();
writer.Close();
}
how i can clear all data in this text file before writeing into this file?
You are using the constructor that instructs the stream to Append to the existing file (or create a new one if it doesn't exist. Just change your constructor to
using(StreamWriter writer = new StreamWriter(filePath, false))
{
.....
}
and it will clear and rewrite your file
MSDN says about the second parameter
appendType: System.Boolean
true to append data to the file; false to overwrite the file. If the
specified file does not exist, this parameter has no effect, and the
constructor creates a new file.
Notice also that you should use the using statement around a Stream to be sure to properly close and dispose the underlying file system resource.
You should be using false as your 2nd parameter instead of true.
StreamWriter writer = new StreamWriter(filePath, false);
True indicates to append the text to the file.
Alternatively, you can also use the following code to remove all text from a file
File.WriteAllText(filePath, String.Empty);

Returning Multiple Files from MVC Action

So I've got an MVC 3 application that has a couple places where a text file gets generated and returned in an action using:
return File(System.Text.Encoding.UTF8.GetBytes(someString),
"text/plain", "Filename.extension");
and this works fabulously. Now i've got a situation where I'm trying to return a pair of files in a similar fashion. On the view, i have an action link like "Click here to get those 2 files" and i'd like both files to be downloaded much like the single file is downloaded in the above code snippet.
How can I achieve this? Been searching around quite a bit and haven't even seen this question posed anywhere...
Building on Yogendra Singh's idea and using DotNetZip:
var outputStream = new MemoryStream();
using (var zip = new ZipFile())
{
zip.AddEntry("file1.txt", "content1");
zip.AddEntry("file2.txt", "content2");
zip.Save(outputStream);
}
outputStream.Position = 0;
return File(outputStream, "application/zip", "filename.zip");
Update 2019/04/10:
As #Alex pointed out, zipping is supported natively since .NET Framework 4.5, from JitBit and others:
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var file1 = archive.CreateEntry("file1.txt");
using (var streamWriter = new StreamWriter(file1.Open()))
{
streamWriter.Write("content1");
}
var file2 = archive.CreateEntry("file2.txt");
using (var streamWriter = new StreamWriter(file2.Open()))
{
streamWriter.Write("content2");
}
}
return File(memoryStream.ToArray(), "application/zip", "Images.zip")
}
Sorry for bumping an old question but...
Another alternative would be to initiate multiple file downloads using JavaScript, and serve files in two different Action Methods on ASP.NET's side.
You're saying you have a link:
On the view, i have an action link like "Click here to get those 2
files"
So make this link like this:
Click to get 2 files
<script src="download.js"></script>
I'm using download.js script found here but you can find plenty of different other options, see this SO question: starting file download with JavaScript for example
I would advice to create a zip file to include both the files using steps(ALGORITHM):
Create a Zip file and add the desired files into the zip
Return the zip file having all desired files from the action
Java Syntax (Just for understanding)
FileOutputStream fos = new FileOutputStream("downloadFile.zip");
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
zos.putNextEntry(new ZipEntry("Filename1.extension"+));
//write data in FileName1.extension
zos.write(contentBuffer1, 0, len);
zos.putNextEntry(new ZipEntry("Filename2.extension"));
//write data in FileName2.extension
zos.write(contentBuffer2, 0, len);
//write other files.....
zos.close();
Once zip file is created, return the newly created zip file to download.
return File("downloadFile.zip");
.DOT Net Equivalent using DotNetZip
var os = new MemoryStream();
using (var zip = new ZipFile())
{
//write the first file into the zip
zip.AddEntry("file1.txt", "content1");
//write the second file into the zip
zip.AddEntry("file2.txt", "content2");
//write other files.....
zip.Save(os);
}
outputStream.Position = 0;
return File(outputStream, "application/zip", "filename.zip");
Hope this helps!
Look at this SO solution: MVC Streaming Zip File
The advantage of this solution is that it streams the file to the client.
I just implemented this solution a couple of days ago and it worked fantastic.

Upload multiple data by excel sheet in database.

Hey Guys I have a database with field
Name,Email,Contact,Address,Profile And I have created a excel sheet with
these field & data in it (100 entry).
Now i want to use File-upload Control for browsing these excel sheet & on click of Button Control it automatically fill excel sheet data into database.
So anybody who has a idea kindly share with me. I try over it a lot but doesn't reach to solution. So please kindly share your valuable guidance.
Take a look at the Excel Data Reader project. With this you can very easily dump an Excel file (either older binary format or newer OpenXML format) into a DataSet, at which point you can do whatever you want with the data. Here's some sample code I used as a proof of concept in a previous project:
private static DataSet ImportDataFromExcelFile()
{
DataSet dataFromExcelFile = null;
// Try importing the file as OpenXML
using (var excelFile = File.Open(excelFileName, FileMode.Open, FileAccess.Read))
{
using (var excelReader = ExcelReaderFactory.CreateOpenXmlReader(excelFile))
{
excelReader.IsFirstRowAsColumnNames = true;
dataFromExcelFile = excelReader.AsDataSet();
}
}
if (dataFromExcelFile != null)
return dataFromExcelFile;
// Try importing the file as Excel 97-2003
using (var excelFile = File.Open(excelFileName, FileMode.Open, FileAccess.Read))
{
using (var excelReader = ExcelReaderFactory.CreateBinaryReader(excelFile))
{
excelReader.IsFirstRowAsColumnNames = true;
dataFromExcelFile = excelReader.AsDataSet();
}
}
if (dataFromExcelFile != null)
return dataFromExcelFile;
throw new Exception("Unable to determine format of Excel file.");
}
First it tries to read the Excel file in the more modern format. If that fails, it tries in an older format. If that fails, it gives up and throws an exception.

ASP.NET to PowerPoint: File gets corrupted when adding image

I have used this example when exporting data to PowerPoint:
I have modified the GenerateSlidesFromDB() method:
public void GenerateSlidesFromDB()
{
string slideName = #"C:\Users\x\Desktop\output.pptx";
File.Copy(#"C:\Users\x\Desktop\Test.pptx", slideName, true);
using (PresentationDocument presentationDocument = PresentationDocument.Open(slideName, true))
{
PresentationPart presentationPart = presentationDocument.PresentationPart;
SlidePart slideTemplate = (SlidePart)presentationPart.GetPartById("rId2");
string firstName = "Test User";
SlidePart newSlide = CloneSlidePart(presentationPart, slideTemplate);
InsertContent(newSlide, firstName);
newSlide.Slide.Save();
DeleteTemplateSlide(presentationPart, slideTemplate);
presentationPart.Presentation.Save();
}
}
As you can see I overwrite the placeholder with "Test User", and it works like a charm.
I need to add an image (as a placeholder) to this pptx-file.
When I do that (and run the code again) I get a corrupted pptx-file?
Error message:
PowerPoint removed unreadable content
in output.pptx. You should review
this presentation to determine whether
any content was unexpectedly changed
or removed.
Edit: If I try the original code (which is slightly modified since I dont have Adventureworks), I get some other kind of error message:
This file may have become corrupt or damaged for the following reasons:
Third-party XML editors sometimes create files that are not compatible with Microsoft Office XML specifications.
The file has been purposely corrupted with the intent to harm your computer or your data.
Be cautious when opening a file from an unknown source.
PowerPoint can attempt to recover data from the file, but some presentation data, such as shapes, text,and formatting, may be lost.
Do one of the following:
If you want to recover data from the file, click Yes.
If you do not want to recoverdata from the file, click No.
Ok, sorry for this useless post. My bad.
Solution:
string imgId = "rIdImg" + i;
ImagePart imagePart = newSlide.AddImagePart(ImagePartType.Jpeg, imgId);
MemoryStream stream3 = new MemoryStream();
using (FileStream file = File.Open(#"C:\Users\x\Desktop\Test.jpg", FileMode.Open))
{
byte[] buffer = new byte[file.Length];
file.Read(buffer, 0, (int)file.Length);
stream3.Write(buffer, 0, buffer.Length);
imagePart.FeedData(new MemoryStream(buffer));
}
SwapPhoto(newSlide, imgId);

Resources