I am trying to send emails with attachments attached to the email. I have a InputFile with a progress bar that I am able to upload files. I have attempted to use the memory stream to make attachments to the MailMessage class. The issue is that when the email is received, I am able to see the attachments but I can't read or view the contents of the attachments. I've posted my code below so you can replicate the issue that I am having (Make sure to install the Meziantou.Framework.ByteSize nuget package)
#using System.Net.Mail
#using System.Globalization
#using Meziantou.Framework
<InputFile OnChange="e => LoadFiles(e)" multiple></InputFile>
#foreach (var file in uploadedFiles)
{
<div>
#file.FileName
<progress value="#file.UploadedBytes" max="#file.Size"></progress>
#file.UploadedPercentage.ToString("F1")%
(#FormatBytes(file.UploadedBytes) / #FormatBytes(file.Size))
</div>
}
<button type="button" #onclick="#HandleNotifSubmit" class="btn btn-primary submit">Send Email</button>
#code {
private MemoryStream fileContents { get; set; }
List<FileUploadProgress> uploadedFiles = new();
MailMessage message = new MailMessage();
StreamWriter writer { get; set; }
private async ValueTask LoadFiles(InputFileChangeEventArgs e)
{
var files = e.GetMultipleFiles(maximumFileCount: 100);
var startIndex = uploadedFiles.Count;
// Add all files to the UI
foreach (var file in files)
{
var progress = new FileUploadProgress(file.Name, file.Size);
uploadedFiles.Add(progress);
}
// We don't want to refresh the UI too frequently,
// So, we use a timer to update the UI every few hundred milliseconds
await using var timer = new Timer(_ => InvokeAsync(() => StateHasChanged()));
timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(500));
// Upload files
byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(4096);
try
{
foreach (var file in files)
{
using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
while (await stream.ReadAsync(buffer) is int read && read > 0)
{
uploadedFiles[startIndex].UploadedBytes += read;
var readData = buffer.AsMemory().Slice(0, read);
}
fileContents = new MemoryStream(buffer);
writer = new StreamWriter(fileContents);
fileContents.Position = 0;
message.Attachments.Add(new Attachment(fileContents, file.Name));
startIndex++;
}
}
finally
{
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
StateHasChanged();
}
}
string FormatBytes(long value)
=> ByteSize.FromBytes(value).ToString("fi2", CultureInfo.CurrentCulture);
record FileUploadProgress(string FileName, long Size)
{
public long UploadedBytes { get; set; }
public double UploadedPercentage => (double)UploadedBytes / (double)Size * 100d;
}
private async void HandleNotifSubmit()
{
try
{
var sClient = new SmtpClient("FAKECOMPANYCLIENT");
sClient.Port = 25;
sClient.UseDefaultCredentials = false;
message.Subject = "Hello World";
message.From = new MailAddress("test#gmail.com");
message.IsBodyHtml = true;
message.To.Add(new MailAddress("Fake#gmail.com"));
message.Body = "Please view attachments below.";
sClient.Send(message);
message.Dispose();
}
catch
{
Console.WriteLine("error");
}
}
}
I have also tried to use a stream writer with no success. I have also tried various ways to do a file.CopyToAsync(memoryStreamname). I am not sure what else I am missing or doing wrong here.
Thank you in advance.
I have a Blazor app where Im using BlazorInputFile from this website - https://blog.stevensanderson.com/2019/09/13/blazor-inputfile/ however the page only loads it to a Memory Stream, not copy the file to a folder on the server. I need it to copy to a folder on the server.
<div class="form-group">
<label for="taskName">Feature Image</label>
<InputFile OnChange="HandleFileSelected" />
</div>
#code {
IFileListEntry file;
void HandleFileSelected(IFileListEntry[] files)
{
file = files.FirstOrDefault();
}
async Task CountLines()
{
numLines = 0;
using (var reader = new System.IO.StreamReader(file.Data))
{
while (await reader.ReadLineAsync() != null)
{
numLines++;
}
}
}
async Task UploadFile()
{
if (file != null)
{
var path = System.IO.Path.Combine(Server.MapPath("~/Uploads/"));
string pathstring = System.IO.Path.Combine(path.ToString());
string filename1 = Guid.NewGuid() + System.IO.Path.GetExtension(file.Name);
bool isexists = System.IO.Directory.Exists(path);
if (!isexists)
{
System.IO.Directory.CreateDirectory(pathstring);
}
string uploadpath = pathstring + "\\" + filename1;
file.SaveAs(uploadpath);
}
}
In the code above I have created a UploadFile method and taken my usual way of uploading files, but obviously it wont work because IFileListEntry does not have the SaveAs method and Server will not work on Blazor.
How am I best uploading this file to the server please? (UploadFile method will get called on form submit).
This must be a dumb question.
I have a web API Rest on c# that have an endpoint to generate and download an excel file. I use NPOI library.
It works fine if the file is generated within the root of my site. But when I try to move my file to a folder, file name include folder and de %2F char. I suppose this is an URL encode problem, but don't know how to fix it. This is my code.
[AllowAnonymous]
[HttpGet("ExportExcel")]
public async Task<IActionResult> ExportExcel(int activos = 1, int idarticulo = -1, string filtro = "", string ordenar = "", int ordenarsentido = 1)
{
var memory = new MemoryStream();
var newFile = #"Export/Articulos.xlsx";
//IArticulosService articulosService = new ArticulosService(_validation.sConnectionString, _validation.sEmpresa);
var mycon = _validation.GetConnectionStringFromClientID("ClientId1");
IArticulosService articulosService = new ArticulosService(mycon, "Ciatema");
try
{
// Genero el excel con el listado
articulosService.ExportarExcel(newFile, activos, filtro, ordenar, ordenarsentido);
using (var stream = new FileStream(newFile, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", newFile);
}
catch (QueryFormatException ex)
{
return BadRequest(ex.Message);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
Donwload filename is: Export%2FArticulos.xlsx when i need it to be only Articulos.xlsx.
Thanks!
you should be passing file name only instead of complete path in your return File method like below
return File(memory, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Path.GetFileName(newFile));
I have a requirement to provide report generation functionality on a button click. I am using Liferay Portal 6.1 with Tomcat 7 as web portal, Liferay Developer Studio (Eclipse Indigo) IDE, iReport (to create report template), and JasperReports library to produce the PDF. The portal is a ticket management system with two entities (Tickets and Documents) which are entered via their respective custom portlets. Both entities, when in edit mode, need the 'View Report' function. I have searched for answers to this problem extensively, picking up a snippet here and there, but nothing I have found is exactly my situation. Upon clicking the 'View Report' button, the user needs to be presented with a formatted PDF where the user can then decide to 'Save', 'Print', or 'Close' from. I also need to pass data to the report so information can be retrieved for the entity that is open (i.e., documentId, ticketId, docType, etc.), but this piece can be added later; once I just get a PDF to open.
Not knowing how to go about implementing this, I decided to use a jQuery Modal to open 'viewReport.jsp' page and process the report there. However, this approach yields a JRException IllegalState: cannot obtain OutputStream because writer is already in use. I have read in my searches that Liferay layout uses OutputStream and that's probably why the writer is already in use. Here's the code I am using:
edit_document.jsp (button and jQuery Modal)
<!-- View Report Button at bottom of form -->
<aui:button type="button" id="viewReportBtn" value="View Report" />
<!-- URL declaration for PDF popup window -->
<portlet:renderURL var="viewReportURL" windowState="<%= LiferayWindowState.EXCLUSIVE.toString() %>" >
<portlet:param name="mvcPath" value="/html/document/viewReport.jsp" />
<portlet:param name="documentId" value="<%= Long.toString(documentID) %>" />
<portlet:param name="ticketId" value="<%= Long.toString(ticketID) %>: />
<portlet:param name="docType" value="<%= docType %>" /> <!-- This is already a String value -->
</portlet:renderURL>
<!-- jQuery to open popup window for PDF -->
<aui:script use="aui-dialog, aui-overlay-manager, dd-constrain" >
var reportDialogOptions = {
title : 'Dialog',
bodyContent : '',
centered : true,
group : default,
height : 800,
width : 1000,
modal : true,
};
$('#viewReportBtn').on('click', finction(event) {
var editFeelingDialog = new A.Dialog(
A.merge(reportDialogOptions, {
title : 'Document View Report'
})
).plugin(A.Plugin.IO,{uri : '<%= viewReportURL %>'}).render();
});
</aui:script>
viewReport.jsp (page to process jrxml template and open PDF)
<!-- viewReport.jsp page to render PDF -->
<%# page contentType = "application/pdf" %>
<%# page trimDirectiveWhitespaces = "true" %>
<%# page import = "net.sf.jasperreports.engine.*" %>
<%# page import = "java.io.File" %>
<%# page import = "java.io.FileInputStream" %>
<%# page import = "java.io.FileNotFoundException" %>
<%# page import = "java.io.InputStream" %>
<%# page import = "java.sql.Connection" %>
<%# page import = "java.sql.SQLException" %>
<%
Connection conn = null;
try
{
String url = "jdbc:oracle:thin:#myDBSRV:1521:myDatabase";
String userName = "myUsername";
String password = "myPassword";
// Connecting to the Oracle database
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(url, username, password);
// Loading the Jasper Report file from local file system\
String jrxmlFile = session.getServletContext().getRealPath(request.getContextPath())+"\\report5.jrxml";
InputStream input = new FileInputStream(new File(jrxmlFile));
// Generate the report
JasperReport jasperReport = JasperCompileManager.compileReport(input);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperPrint, null, conn);
// Export the report as a PDF
JasperExportManager.exportReportToPdfStream(jasperPrint, response.getOutputStream());
}
catch (FileNotFoundExcecption ex)
{
System.out.println(ex.getMessage());
ex.printStakeTrace();
}
catch (JRException ex)
{
System.out.println(ex.getMessage());
ex.printStakeTrace();
}
catch (ClassNotFoundException ex)
{
System.out.println(ex.getMessage());
ex.printStakeTrace();
}
catch (SQLException ex)
{
System.out.println(ex.getMessage());
ex.printStakeTrace();
}
finally
{
if (conn != null)
{
conn.close();
}
}
%>
I also tried to add java methods to my DocumentPortlet.java class and call the method(s) when the button is clicked, but I am not that familiar enough with ajax to get it right:
DocumentPortlet.java
public class DocumentPortlet extends MVCPortlet{
Connection conn = null;
// More methods for saving, editing, and deleting documents are here...
public void generateReport()
{
initConnection();
showReport();
}
public void initConnection()
{
String host = "jdbc:oracle:thin:#ncimsdbsrv:1521:ncimsdev";
String userName = "lportal";
String password = "NSS4207itnp";
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex)
{
ex.printStackTrace();
}
try
{
conn = DriverManager.getConnection(host, userName, password);
}
catch (SQLException ex)
{
ex.printStackTrace();
}
}
public void showReport()
{
//Path to your .jrxml file
String reportName = "[path to file]";
//Get a stream to read the file
InputStream is = this.getClass().getClassLoader().getResourceAsStream(reportName);
try
{
//Fill the report with parameter, connection, and stream reader
JasperPrint jp = JasperFillManager.fillReport(is, null, session);
//Viewer for Jasper report
JRViewer jv = new JRViewer(jp);
//Insert viewer to a JFrame to make it showable
JFrame jf = new JFrame();
jf.getContentPane().add(jv);
jf.validate();
jf.setVisible(true);
jf.setSize(new Dimension(800, 600));
jf.setLocation(300, 100);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
catch (JRException ex)
{
ex.printStakeTrace();
}
finally
{
closeSession(session);
}
}
}
How can I provide this functionality in a way that my users can click the button and a PDF be generated on their screen? I need a clear and concise solution to this problem. Thank you!
Lee
In case anyone is interested, I found an answer on a Liferay forum. My implementation was not correct. So now I have this in my
viewDocument.jsp:
<!-- View Report button at bottom of form -->
<aui:button id="viewReportBtn" value="View Report" />
<!-- URL declaration for PDF generation -->
<portlet:resourceURL id="generatePDF" var="generateReportURL" >
<portlet:param name="jspPage" value="/html/document/viewReport.jsp" />
<portlet:param name="docType" value="<%= docType %>" />
</portlet:resourceURL>
<!-- AJAX call to resource request handler in DocumentPortlet.java file -->
<aui:script use="aui-dialog, aui-overlay-manager, dd-constrain" >
$(document).ready(function()
{
$('#viewReportBtn').click(function(event)
{
$.ajax('<%= generateReportURL %>');
}
});
I also created the ResourceRequestHandler in my Portlet java file like so:
DocumentPortlet.java:
public class DocumentPortlet extends MVCPortlet{
Connection conn = null;
// More methods to add, update, and delete here
#Override
public void serveResource(ResourceRequest request, ResourceResponse response)
throws IOException, PortletException
{
String url = "jdbc:oracle:thin:#myDatabaseSrv:1521:myDatabaseSid";
String user = "myUserName";
String pass = "myPassword";
String docType = ParamUtil.getString(request, "docType");
try
{
// Connect to database
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(url, user, pass);
// Load JRXml file from local file system
String jrxmlFile = "C:\\Reports\\report5.jrxml";
InputStream input = new FileInputStream(new File(jrxmlFile));
// Generate report
JasperReport jasperReport = JasperCompileManager.compileReport(input);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperPrint, null, conn);
/***** Display PDF in Adobe Reader *****/
// Create new OutputStream where data is written to byte array[]
ByteArrayOutputStream output = new ByteArrayOutputStream();
// Export the report to PDF format
JasperExportManager.exportReportToPdfStream(jasperPrint, output);
// Create a String for thr report title
final String fileName = docType + ".pdf";
//Create a new stream and call the report
OutputStream pdfFile = new FileOutputStream(new File(fileName));
//Write the PDF
pdfFile.write(output.toByteArray());
//Clean the PDF
pdfFile.flush();
//Close it
pdfFile.close();
//Run the brand new PDF report
final String PDF_RUN = "rundll32 url.dll, FileProtocolHandaler " + fileName;
//Execute the command
Runtime.getRuntime().exec(PDF_RUN);
}
catch(FileNotFoundException ex)
{
System.out.println("File not found on file system, check file path!!" + ex.getMessage());
ex.printStackTrace();
}
catch(JRException ex)
{
System.out.println("Error in Jasper Report!!" + ex.getMessage());
ex.printStackTrace();
}
catch(ClassNotFoundException ex)
{
System.out.println("Oracle Class for driver not found!!" + ex.getMessage());
ex.printStackTrace();
}
catch(SQLException ex)
{
System.out.println("Error getting connection to database!!" + ex.getMessage());
ex.printStackTrace();
}
}
}
This eliminated the IllegalStateException I was getting and will open the pdf in Adobe Reader just fine, however, it opens it up on the server. Therefore, a problem still remains. Anybody know how I can get this pdf to open on the client machine?
Iam using JSF2.1 along with primefaces for uploading file.In my application i have to add the file dynamically on creating a record.But when i use
i cant write the code for uploading my file during save.I want the file to be uploaded on click of save only and not during upload.
Can anyone help me how to implement this
public String handleFileUpload(FileUploadEvent event) throws IOException {
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
UploadedFile file = event.getFile();
String prefix = FilenameUtils.getBaseName(file.getFileName());
String suffix = FilenameUtils.getExtension(file.getFileName());
String path = "C:/apache-tomcat-7.0.47/webapps/ROOT/WEB-INF/images";
ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
File fileToDirectory = File.createTempFile(prefix + "-", "." + suffix, new File(path));
InputStream inputStream = event.getFile().getInputstream();
String fileName = event.getFile().getFileName();
OutputStream outputStream = new FileOutputStream(fileToDirectory);
byte[] buffer = new byte[1024];
int length;
//copy the file content in bytes
while ((length = inputStream.read(buffer)) > 0){
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
return path+fileName;
}
I need to have this code on save but i cant get event during save
That isn't possible with auto mode. Use the basic mode instead. Then you can bind the input value to an UploadedFile property directly. This only requires disabling Ajax.
E.g.
<h:form enctype="multipart/form-data">
...
<p:fileUpload mode="simple" value="#{bean.file}" />
...
<p:commandButton value="Save" action="#{bean.save}" ajax="false" />
</h:form>
with
private UploadedFile file; // +getter+setter
public void save() {
try (InputStream input = file.getInputStream()) {
// ...
}
}
The alternative is to migrate to standard JSF <h:inputFile> component which was introduced in JSF 2.2. Then you can continue using Ajax.
E.g.
<h:form enctype="multipart/form-data">
...
<h:inputFile value="#{bean.file}" />
...
<p:commandButton value="Save" action="#{bean.save}" />
</h:form>
with
private Part file; // +getter+setter
public void save() {
try (InputStream input = file.getInputStream()) {
// ...
}
}
See also:
How to upload file using JSF 2.2 <h:inputFile>? Where is the saved File?