Error in File downloading file from a folder in MVC2 - asp.net-mvc

I am encountering a problem in getting the download prompt. In the below code first am allowing the user to upload a file to compress. Once the file is compressed the user should be provided with the compressed files. But in the below code download prompt doesn't appears neither it shows any error. Please help me by correcting my code
The view code:
function CompressFile(box) {
var file = document.getElementById('fileComp');
if (file.value == "") {
alert("Choose a file to upload");
return false;
}
dhtmlx.modalbox.hide(box);
var fd = new FormData();
fd.append('file', file.files[0]);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/FileUpload/Compress', true);
xhr.send(fd);
}
The controller code:
public ActionResult Compress(HttpPostedFileBase file)
{
var supportedType = new[] { "pdf" };
var fileExt = System.IO.Path.GetExtension(file.FileName).Substring(1);
var filename = Path.GetFileNameWithoutExtension(file.FileName) ?? "";
if (file.ContentLength > 0 && supportedType.Contains(fileExt))
{
string filePath = Path.Combine(HttpContext.Server.MapPath(_uploadPDF), Path.GetFileName(file.FileName));
file.SaveAs(filePath);
PdfReader reader = new PdfReader(filePath);
string name = DateTime.Now.ToString("ddMM_HHmmss");
name = Server.MapPath(_fileUploadPath + name + ".pdf");
PdfStamper stamper = new PdfStamper(reader, new FileStream(name, FileMode.Create), PdfWriter.VERSION_1_5);
stamper.FormFlattening = true;
stamper.SetFullCompression();
stamper.Close();
string fn = System.IO.Path.GetFileName(name);
return base.File(name, "application/pdf",fn);
}
else
{
return View();
}
}

The problem is that you're using Ajax. You can't download a file through Ajax. You need to do a regular POST to the ActionMethod. That way the browser can send you back the file and prompt the user where he wants to save it.

Related

How to Upload a Profile photo in base64 format For Community Users Using ConnectApi.UserProfiles.setPhoto

1Am Uploading Profile Photo for Community Users in base64 format By Using ConnectApi.UserProfiles.setPhoto Method. But am getting "ConnectApi.ConnectApiException: The file you uploaded doesn't appear to be a valid image" This error, Help me to Fix this issue.
Hi you can try the below method:
public PageReference upload() {
Blob b;
document.AuthorId = UserInfo.getUserId();
document.FolderId = UserInfo.getUserId(); // put it in running user's folder
try {
document.type = 'jpg';
document.IsPublic = true;
insert document;
// ImageId = '06990000001HnuB';
b = document.Body;
//ConnectApi.ChatterUsers newPhoto = new ConnectApi.ChatterUsers();
} catch (DMLException e) {
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, 'Error uploading file'));
return null;
} finally {
document.body = null; // clears the viewstate
document = new Document();
}
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.INFO, 'File uploaded successfully : ' + b));
String communityId = null;
String userId = UserInfo.getUserId();
//ID fileId = ImageId;
// Set photo
ConnectApi.Photo photo = ConnectApi.ChatterUsers.setPhoto(communityId, userId, new ConnectApi.BinaryInput(b, 'image/jpg', 'userImage.jpg'));
return null;
}
I was getting the same error, there isn't too much detail so I'm not too sure about your problem. I solved the problem using the code below. Modify as needed.
public static Boolean updateUserProfilePic(String userProfilePicString, String userId, String fileType){
Boolean updateSuccessful = true;
System.debug('-------------' + userProfilePicString.length());
try{
Blob blobImage = EncodingUtil.base64Decode(userProfilePicString);
ConnectApi.BinaryInput fileUpload = new ConnectApi.BinaryInput(blobImage, 'image/jpg', 'userImage.jpg');
ConnectApi.Photo photoProfile = ConnectApi.UserProfiles.setPhoto(null, userId, fileUpload);
}
catch(Exception exc){
updateSuccessful = false;
}
return updateSuccessful;
}

Object reference not set to an object while file upload in OneDrive

I am using Microsoft Graph SDK to upload file in chunks in OneDrive. I am using below code to upload the file:
try
{
GraphServiceClient graphClient = this.GetGraphServiceClient(accessToken);
string fileName = Path.GetFileName(srcFilePath);
using (var fileContentStream = System.IO.File.Open(srcFilePath, System.IO.FileMode.Open))
{
var uploadSession = await graphClient.Me.Drive.Root.ItemWithPath(fileName).CreateUploadSession().Request().PostAsync();
var maxChunkSize = 5 * 1024 * 1024;
var provider = new ChunkedUploadProvider(uploadSession, graphClient, fileContentStream, maxChunkSize);
var chunkRequests = provider.GetUploadChunkRequests();
var readBuffer = new byte[maxChunkSize];
var trackedExceptions = new List<Exception>();
Microsoft.Graph.DriveItem itemResult = null;
foreach (var request in chunkRequests)
{
var result = await provider.GetChunkRequestResponseAsync(request, readBuffer, trackedExceptions);
if (result.UploadSucceeded)
{
itemResult = result.ItemResponse;
}
}
}
}
catch (Microsoft.Graph.ServiceException e)
{
}
catch (Exception ex)
{
}
The above code works fine with normal file names. However, when I am trying to upload a file with name as Test#123.pdf, "Object reference not set to an object" exception is thrown at line var provider = new ChunkedUploadProvider(uploadSession, graphClient, fileContentStream, maxChunkSize); Please see below screenshot:
Is this a limitation of OneDrive SDK, or am I not passing the parameters correctly?
The # sign has a special meaning in a URL. Before you can use it, you'll need to URL Encode the file name: Test%23123.pdf.

Trouble exporting to excel from mvc action

I created a simple action to download some content as excel file:
public FileResult ExportToExcel()
{
string filename = "list.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
List<string[]> list = new List<string[]>();
list.Add(new[] { "col1", "col2", "cols3" });
list.Add(new[] { "col4", "col5", "cols6" });
list.Add(new[] { "col7", "col8", "cols9" });
StringWriter sw = new StringWriter();
sw.WriteLine("ID,Date,Description");
foreach (string[] item in list)
{
sw.WriteLine("{0},{1},{2}", item[0], item[1], item[2]);
}
byte[] fileContents = Encoding.UTF8.GetBytes(sw.ToString());
return this.File(fileContents, contentType, filename);
}
I have 2 issues with it:
1. The file is downloaded but I cannot open it and am getting a warning:
Excel cannot open the file ... because the file format or file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.
When I use old excel format:
string filename = "List.xls";
string contentType = "application/vnd.ms-excel";
I am able to open the file but after 3 different warnings about file being corrupted etc.
Btw I compared saving and tried to write file as pdf
string filename = "List.pdf";
string contentType = "application/pdf";
And I still couldn't open the file - it said format is not valid etc.
2. The contents appear in the file in the second example however the commas are not recognised as column separators and all data in a row is written as one column.
What separator to use for excel format or how to write data to file to have it in a table excel format?
Ideal solution for me would be just return exported view (strongly typed) but I didn't find out how to do it so far.
--- EDIT: Working solution ---
public FileResult ExportToExcel()
{
string filename = "List.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
List<string[]> titles = new List<string[]>() { new[] { "a", "be", "ce" } };
List<string[]> list = new List<string[]>
{
new[] { "col1", "col2", "cols3" },
new[] { "col4", "col5", "cols6" },
new[] { "col7", "col8", "cols9" },
new[] { "col10", "col11", "cols12" }
};
XLWorkbook wb = new XLWorkbook();
XLTables xt = new XLTables();
var ws = wb.Worksheets.Add("List");
ws.Cell(1, 1).InsertData(titles);
ws.Cell(2, 1).InsertData(list);
ws.Columns().AdjustToContents();
var stream = new MemoryStream();
wb.SaveAs(stream);
stream.Seek(0, SeekOrigin.Begin);
wb.Dispose();
return this.File(stream, contentType, filename);
}
The reason why it is not being correctly rendered is because you cannot just return the mime type and expect the framework to figure out the rest.
I would go with a nuget package called closedXML which will allow you to create an excel file in memory and stream it back to the client.
it comes with a full documentation (here) for more information.
Using this package you can do something like
XLWorkbook wb = new XLWorkbook();
XLTables xt = new XLTables();
var ws = wb.Worksheets.Add("Sheet 1");
var firstCell = ws.Cell(1, 1);
var lastCell = ws.Cell(3, list.Count);
var table = ws.Range(firstCell.Address, lastCell.Address).AsTable();
table.Cell(2, 1).InsertData(list);
table.CreateTable();
ws.Columns().AdjustToContents();
using(var stream = new MemoryStream())
{
wb.SaveAs(stream);
stream.Seek(0, SeekOrigin.Begin);
wb.Dispose();
return File(stream , contentType, filename);
}

Not able to properly download files from azure storage and data are lost too when downloading files

I have 2 files saved on Azure blob storage:
Abc.txt
Pqr.docx
Now i want to create zip files of this 2 files and allow user to download.
I have saved this in my database table field like this:
Document
Abc,Pqr
Now when i click on download then i am getting file like below with no data in it and file extension are lost too like below:
I want user to get exact file(.txt,.docx) in zip when user download zip file.
This is my code:
public ActionResult DownloadImagefilesAsZip()
{
string documentUrl = repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
if (!string.IsNullOrEmpty(documentUrl))
{
string[] str = documentUrl.Split(',');
if (str.Length > 1)
{
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
if (!string.IsNullOrEmpty(t))
{
Stream s = this.GetFileContent(t);
zip.AddEntry("File" + cnt, s);
}
cnt++;
}
zip.Save(outputStream);
outputStream.Position = 0;
return File(outputStream, "application/zip", "all.zip");
}
}
}
public Stream GetFileContent(string fileName)
{
CloudBlobContainer container = this.GetCloudBlobContainer();
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
var stream = new MemoryStream();
blockBlob.DownloadToStream(stream);
return stream;
}
public CloudBlobContainer GetCloudBlobContainer()
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"].ToString());
CloudBlobClient blobclient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer blobcontainer = blobclient.GetContainerReference("Mystorage");
if (blobcontainer.CreateIfNotExists())
{
blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
}
blobcontainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
return blobcontainer;
}
I want same file to be downloaded when user download zip file.
Can anybody help me with this??
I'm not a web dev, but hopefully this will help. This snippet of code is in a method where I download a list of blobs into a zip file archive using a stream. The list of files had the slashes in all directions, so there's code in here to fix this, and to make sure I'm getting the blob reference with the right text (no URL, and no opening slash if the blob is in a "folder").
I suspect your problem is not using a memory stream or a binary writer. Specificity helps sometimes. Good luck.
using (ZipArchive zipFile = ZipFile.Open(outputZipFileName, ZipArchiveMode.Create))
{
foreach (string oneFile in listOfFiles)
{
//Need the filename, complete with relative path. Make it like a file name on disk, with backwards slashes.
//Also must be relative, so can't start with a slash. Remove if found.
string filenameInArchive = oneFile.Replace(#"/", #"\");
if (filenameInArchive.Substring(0, 1) == #"\")
filenameInArchive = filenameInArchive.Substring(1, filenameInArchive.Length - 1);
//blob needs slashes in opposite direction
string blobFile = oneFile.Replace(#"\", #"/");
//take first slash off of the (folder + file name) to access it directly in blob storage
if (blobFile.Substring(0, 1) == #"/")
blobFile = oneFile.Substring(1, oneFile.Length - 1);
var cloudBlockBlob = this.BlobStorageSource.GetBlobRef(blobFile);
if (!cloudBlockBlob.Exists()) //checking just in case
{
//go to the next file
//should probably trace log this
//add the file name with the fixed slashes rather than the raw, messed-up one
// so anyone looking at the list of files not found doesn't think it's because
// the slashes are different
filesNotFound.Add(blobFile);
}
else
{
//blob listing has files with forward slashes; that's what the zip file requires
//also, first character should not be a slash (removed it above)
ZipArchiveEntry newEntry = zipFile.CreateEntry(filenameInArchive, CompressionLevel.Optimal);
using (MemoryStream ms = new MemoryStream())
{
//download the blob to a memory stream
cloudBlockBlob.DownloadToStream(ms);
//write to the newEntry using a BinaryWriter and copying it 4k at a time
using (BinaryWriter entry = new BinaryWriter(newEntry.Open()))
{
//reset the memory stream's position to 0 and copy it to the zip stream in 4k chunks
//this keeps the process from taking up a ton of memory
ms.Position = 0;
byte[] buffer = new byte[4096];
bool copying = true;
while (copying)
{
int bytesRead = ms.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
entry.Write(buffer, 0, bytesRead);
}
else
{
entry.Flush();
copying = false;
}
}
}//end using for BinaryWriter
}//end using for MemoryStream
}//if file exists in blob storage
}//end foreach file
} //end of using ZipFileArchive
There are two things I noticed:
Once you read the blob contents in stream, you are not resetting that stream's position to 0. Thus all files in your zip are of zero bytes.
When calling AddEntry, you may want to specify the name of the blob there instead of "File"+cnt.
Please look at the code below. It's a console app that creates the zip file and writes it on the local file system.
static void SaveBlobsToZip()
{
string[] str = new string[] { "CodePlex.png", "DocumentDB.png" };
var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("images");
using (var fs = new FileStream("D:\\output.zip", FileMode.Create))
{
fs.Position = 0;
using (var ms1 = new MemoryStream())
{
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
var ms = new MemoryStream();
container.GetBlockBlobReference(t).DownloadToStream(ms);
ms.Position = 0;//This was missing from your code
zip.AddEntry(t, ms);//You may want to give the name of the blob here.
cnt++;
}
zip.Save(ms1);
}
ms1.Position = 0;
ms1.CopyTo(fs);
}
}
}
UPDATE
Here's the code in the MVC application (though I am not sure it is the best code :) but it works). I modified your code a little bit.
public ActionResult DownloadImagefilesAsZip()
{
string[] str = new string[] { "CodePlex.png", "DocumentDB.png" }; //repossitory.GetDocumentsUrlbyId(id);//output:Abc.txt,Pqr.Docx
CloudBlobContainer blobcontainer = GetCloudBlobContainer();// azureStorageUtility.GetCloudBlobContainer();
MemoryStream ms1 = new MemoryStream();
using (ZipFile zip = new ZipFile())
{
int cnt = 0;
foreach (string t in str)
{
var ms = new MemoryStream();
CloudBlockBlob blockBlob = blobcontainer.GetBlockBlobReference(t);
blockBlob.DownloadToStream(ms);
ms.Position = 0;//This was missing from your code
zip.AddEntry(t, ms);//You may want to give the name of the blob here.
cnt++;
}
zip.Save(ms1);
}
ms1.Position = 0;
return File(ms1, "application/zip", "all.zip");
}
I have seen people using ICSharpZip library, take a look at this piece of code
public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
{
using (var zipOutputStream = new ZipOutputStream(response.OutputStream))
{
zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
response.BufferOutput = false;
response.AddHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.ContentType = "application/octet-stream";
foreach (var file in files)
{
var entry = new ZipEntry(file.FilenameSlug())
{
DateTime = DateTime.Now,
Size = file.Filesize
};
zipOutputStream.PutNextEntry(entry);
storageService.ReadToStream(file, zipOutputStream);
response.Flush();
if (!response.IsClientConnected)
{
break;
}
}
zipOutputStream.Finish();
zipOutputStream.Close();
}
response.End();
}
Taken from here generate a Zip file from azure blob storage files

can eml directly open with outlook, instead eml file will download then click it to open in outlook?

I have an asp.net MVC application, below code works file.
But the code is that, When navigate to Email action in browser, an EML file is download, then when we click on that file, the file will open with outlook.
Can it be possible, when action calls, then EML file will directly open with outlook, instead of download and then click to open??
Code
public async Task<FileStreamResult> Email()
{
string dummyEmail = "test#localhost.com";
var mailMessage = new MailMessage();
mailMessage.From = new MailAddress(dummyEmail);
mailMessage.To.Add("dejan.caric#gmail.com");
mailMessage.Subject = "Test subject";
mailMessage.Body = "Test body";
// mark as draft
mailMessage.Headers.Add("X-Unsent", "1");
// download image and save it as attachment
using (var httpClient = new HttpClient())
{
var imageStream = await httpClient.GetStreamAsync(new Uri("http://dcaric.com/favicon.ico"));
mailMessage.Attachments.Add(new Attachment(imageStream, "favicon.ico"));
}
var stream = new MemoryStream();
ToEmlStream(mailMessage, stream, dummyEmail);
stream.Position = 0;
return File(stream, "message/rfc822", "test_email.eml");
}
private void ToEmlStream(MailMessage msg, Stream str, string dummyEmail)
{
using (var client = new SmtpClient())
{
var id = Guid.NewGuid();
var tempFolder = Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name);
tempFolder = Path.Combine(tempFolder, "MailMessageToEMLTemp");
// create a temp folder to hold just this .eml file so that we can find it easily.
tempFolder = Path.Combine(tempFolder, id.ToString());
if (!Directory.Exists(tempFolder))
{
Directory.CreateDirectory(tempFolder);
}
client.UseDefaultCredentials = true;
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = tempFolder;
client.Send(msg);
// tempFolder should contain 1 eml file
var filePath = Directory.GetFiles(tempFolder).Single();
// create new file and remove all lines that start with 'X-Sender:' or 'From:'
string newFile = Path.Combine(tempFolder, "modified.eml");
using (var sr = new StreamReader(filePath))
{
using (var sw = new StreamWriter(newFile))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (!line.StartsWith("X-Sender:") &&
!line.StartsWith("From:") &&
// dummy email which is used if receiver address is empty
!line.StartsWith("X-Receiver: " + dummyEmail) &&
// dummy email which is used if receiver address is empty
!line.StartsWith("To: " + dummyEmail))
{
sw.WriteLine(line);
}
}
}
}
// stream out the contents
using (var fs = new FileStream(newFile, FileMode.Open))
{
fs.CopyTo(str);
}
}
}
With Chrome you can make it automatically open certain files, once they are downloaded.
.EML should attempt to open in Outlook.
I am not sure about other browsers, but Chrome seemed to be the only one with this option.
It's not a pefect solution because if someone downloaded an .EML from another website in Chrome, it will open automatically aswell.
I recommend having Chrome dedicated to your Web application.
You sure can open local .eml file with Outlook.
But in context of web application, you must firstly download it.

Resources