File Upload

R

rn5a

I came across this article titled Pure ASP File Upload at
http://www.asp101.com/articles/jacob/scriptupload.asp & tried it on my
local IIS5.1. Intranet server & it worked. I saved the necessary data
in a MS-Access database file (& not in the hard disk).

Can someone please tell me how do I mail the uploaded file as an
attachment using CDO.MESSAGE?

I tried getting in touch with Jacob, author of the article at the
above URL, by e-mailing him at his e-mail id (which is given in the
article) but unfortunately, the mail bounced back saying delivery
failed!

Thanks
 
D

Dave Anderson

I came across this article titled Pure ASP File Upload at
http://www.asp101.com/articles/jacob/scriptupload.asp & tried
it on my local IIS5.1. Intranet server & it worked. I saved the
necessary data in a MS-Access database file (& not in the hard
disk).

Can someone please tell me how do I mail the uploaded file as
an attachment using CDO.MESSAGE?

This is fairly simple. You need an ASP script that will stream the file to a
browser, based on some key to the DB record. Then you construct a URL for
use in the AddAttachment method of the CDO.Message object.

IMessage.AddAttachment(URL,[UserName],[Password]) ...

"The URL prefixes supported in the URL parameter are file://,
ftp://, http://, and https://. The default prefix is file://.
This facilitates designation of paths starting with drive
letters and of universal naming convention (UNC) paths."

http://msdn2.microsoft.com/en-us/library/ms526983.aspx


To give you an example, here is a segment of code from a feedback form I
use. In it, I call a stored procedure that returns a list of files to be
attached to the message, then makes successive calls to AddAttachment():

for (CN.File_ListByRequest(GUID,"Feedback","Attachment",RS); !RS.EOF;
RS.MoveNext())
MSG.AddAttachment([My script URL] + "?RecordID=" +
RS.Fields("RecordID").Value)

Those calls, in turn, trigger this script (excerpted, of course):

var RecordID = Request.QueryString("RecordID").Item,
CN = Server.CreateObject("ADODB.Connection"),
RS = Server.CreateObject("ADODB.Recordset")
CN.Open(*** my connection string ***)
CN.File_Retrieve(RecordID,RS)
if (!RS.EOF) {
Response.AddHeader("Content-Length",RS.Fields("FileSize").Value)
Response.AddHeader("Content-Disposition","inline; filename=" +
RS.Fields("FileName").Value + ";")
Response.ContentType = RS.Fields("ContentType").Value
Response.BinaryWrite(RS.Fields("Data").Value)
}
RS.Close()
CN.Close()

As you may have guessed, these are JScript examples.
 
A

Anthony Jones

Dave Anderson said:
I came across this article titled Pure ASP File Upload at
http://www.asp101.com/articles/jacob/scriptupload.asp & tried
it on my local IIS5.1. Intranet server & it worked. I saved the
necessary data in a MS-Access database file (& not in the hard
disk).

Can someone please tell me how do I mail the uploaded file as
an attachment using CDO.MESSAGE?

This is fairly simple. You need an ASP script that will stream the file to a
browser, based on some key to the DB record. Then you construct a URL for
use in the AddAttachment method of the CDO.Message object.

IMessage.AddAttachment(URL,[UserName],[Password]) ...

"The URL prefixes supported in the URL parameter are file://,
ftp://, http://, and https://. The default prefix is file://.
This facilitates designation of paths starting with drive
letters and of universal naming convention (UNC) paths."

http://msdn2.microsoft.com/en-us/library/ms526983.aspx


To give you an example, here is a segment of code from a feedback form I
use. In it, I call a stored procedure that returns a list of files to be
attached to the message, then makes successive calls to AddAttachment():

for (CN.File_ListByRequest(GUID,"Feedback","Attachment",RS); !RS.EOF;
RS.MoveNext())
MSG.AddAttachment([My script URL] + "?RecordID=" +
RS.Fields("RecordID").Value)

Those calls, in turn, trigger this script (excerpted, of course):

var RecordID = Request.QueryString("RecordID").Item,
CN = Server.CreateObject("ADODB.Connection"),
RS = Server.CreateObject("ADODB.Recordset")
CN.Open(*** my connection string ***)
CN.File_Retrieve(RecordID,RS)
if (!RS.EOF) {
Response.AddHeader("Content-Length",RS.Fields("FileSize").Value)
Response.AddHeader("Content-Disposition","inline; filename=" +
RS.Fields("FileName").Value + ";")
Response.ContentType = RS.Fields("ContentType").Value
Response.BinaryWrite(RS.Fields("Data").Value)
}
RS.Close()
CN.Close()

As you may have guessed, these are JScript examples.

Alternatively, instead of using a second request to the server, create the
attachment 'manually' like this:-

CN2 = Server.CreateObject("ADODB.Connection")
CN2.Open(*** my connection string ***)
RS2 = Server.CreateObject("ADODB.Recordset")

for (CN.File_ListByRequest(GUID,"Feedback","Attachment",RS); !RS.EOF;
RS.MoveNext())
{
CN2.File_Retrieve(RS.Fields("RecordID").Value, RS2)
if (!RS2.EOF) addAttachment(MSG, RS)
RS2.Close()
}
CN2.Close()

function addAttachment(voMsg, voRS)
{
var oPart = oMsg.Attachments.Add()
oPart.ContentMediaType = voRS.Fields("ContentType").Value
oPart.ContentTransferEncoding = "base64"
oPart.Fields("urn:schemas:mailheader:content-disposition") =
'attachment; filename="' + voRS.Fields("FileName").Value + '"'
oPart.Fields.Update()

var oStream = oPart.GetDecodedContentStream()

oStream.Write(voRS.Fields("Data").Value)
oStream.Flush()
}


Use of an HTTP URL in AddAttachment when running in ASP is undesirable since
CDO uses the WinINET api to perform the fetch. WinInet is not thread safe.
Self referencing URLs can also be a problem if used too often.

BTW, Don't use AddHeader to add a Content-Length header, IIS/ASP will handle
it so at best you're duplicating a header.
 
D

Dave Anderson

Anthony said:
Use of an HTTP URL in AddAttachment when running in ASP is
undesirable since CDO uses the WinINET api to perform the
fetch. WinInet is not thread safe. Self referencing URLs
can also be a problem if used too often.

Nice explanation, Anthony. I have since incorporated this technique.
 
D

Dave Anderson

function addAttachment(voMsg, voRS)
{
var oPart = oMsg.Attachments.Add()
oPart.ContentMediaType = voRS.Fields("ContentType").Value
oPart.ContentTransferEncoding = "base64"
oPart.Fields("urn:schemas:mailheader:content-disposition") =
'attachment; filename="' + voRS.Fields("FileName").Value + '"'
oPart.Fields.Update()

var oStream = oPart.GetDecodedContentStream()

oStream.Write(voRS.Fields("Data").Value)
oStream.Flush()
}


Use of an HTTP URL in AddAttachment when running in ASP is undesirable
since CDO uses the WinINET api to perform the fetch. WinInet is not
thread safe. Self referencing URLs can also be a problem if used too
often.

When certain file types are attached this way (HTML documents, for example),
the following error occurs:

ADODB.Connection error '800a0e79'
Operation is not allowed when the object is open.

The line in question is the Stream.Write() line.

How can this be overcome?
 
A

Anthony Jones

Dave Anderson said:
When certain file types are attached this way (HTML documents, for example),
the following error occurs:

ADODB.Connection error '800a0e79'
Operation is not allowed when the object is open.

The line in question is the Stream.Write() line.

How can this be overcome?

Strange I've never seen that before in this context.

First experiment would be to split the stream write line:-

Dim fieldValue
fieldValue = voRS.Fields("Data").Value
oStream.Write(fieldValue)

That would highlight which of the two ADODB components the error is coming
from but thats just to ensure we're not chasing down the wrong path because
its almost certain the error is a result of accessing the value of the data
field.

The only reason I can guess at for this error is that for some reason
accessing the field is asking the connection to retrieve another stream of
data before its finished with the current one. I'd make sure the recordset
openned is a forward only recordset, that the Data field is the last field
in the field list and that consuming the data field's contents is the last
thing I do with that record before moving on.

The only time I've had CDO object to this approach is when trying to attach
another eml in which case the content type is message/rfc822, it refuses to
use base64 encoding and requires a text based encoding. Hence another
clutching a straws exercise might be to see if a failing attachment works if
the content type is set to application/octet-stream. However I honest don't
think that is the problem.
 
D

Dave Anderson

Anthony Jones said:
First experiment would be to split the stream write line:-

Dim fieldValue
fieldValue = voRS.Fields("Data").Value
oStream.Write(fieldValue)

I should have been more clear. I have already isolated it to Stream.Write()
The only time I've had CDO object to this approach is when trying to
attach another eml in which case the content type is message/rfc822,
it refuses to use base64 encoding and requires a text based encoding.
Hence another clutching a straws exercise might be to see if a failing
attachment works if the content type is set to application/octet-stream.
However I honest don't think that is the problem.

Either way I look at it, I have a problem. I can attempt to attach files
with Message.Attachments.Add() and streams, then start over with
Message.AddAttachment() if it fails, but that isn't a very satisfying
compromise.
 
A

Anthony Jones

Dave Anderson said:
I should have been more clear. I have already isolated it to Stream.Write()

Either way I look at it, I have a problem. I can attempt to attach files
with Message.Attachments.Add() and streams, then start over with
Message.AddAttachment() if it fails, but that isn't a very satisfying
compromise.

Is there a small(ish) repeatable re-production for this problem or is it
more random and intermittent?
I use the technique a lot although I'm not sure I've ever attached HTML
content. What other types of content gets it upset? It could be that CDO
treats HTML as a special case for some reason.
 
D

Dave Anderson

Anthony Jones said:
Is there a small(ish) repeatable re-production for this problem or
is it more random and intermittent?

Those are not mutually exclusive options, Anthony. It's not especially small
to create an architecture to insert files into a database and to extract
them upon form submission. But the symptoms do not appear to be random or
intermittent. I can repeat it at will.
I use the technique a lot although I'm not sure I've ever attached
HTML content. What other types of content gets it upset? It could
be that CDO treats HTML as a special case for some reason.

It appears to happen for all .txt, .htm, .html, or .xml files. Change the
extension on any of them to .log or .asp, for example, and the same file
goes through without error.
 
D

Dave Anderson

I said:
It appears to happen for all .txt, .htm, .html, or .xml files. Change
the extension on any of them to .log or .asp, for example, and the
same file goes through without error.

You know, this got me thinking, so I looked in the database, and each of the
affected ContentType values were of the text/xxx variety. Recalling you
advice about Response.AddHeader (and some ancient memory in my head about
how Mac IE5 used to send resource forks along with uploads), I asked what
business it was of mine to tell the client how to handle the file. I removed
the Part.ContentMediaType assignment, and everything sailed through as
desired.

Seems fair, no?
 
A

Anthony Jones

Dave Anderson said:
You know, this got me thinking, so I looked in the database, and each of the
affected ContentType values were of the text/xxx variety. Recalling you
advice about Response.AddHeader (and some ancient memory in my head about
how Mac IE5 used to send resource forks along with uploads), I asked what
business it was of mine to tell the client how to handle the file. I removed
the Part.ContentMediaType assignment, and everything sailed through as
desired.

Seems fair, no?

As long as the file extension of the filename in the content-disposition
matches the content of the body part the client ought to handle it ok.
Personally I would much prefer the more explicit Content-Type header be
present.

The error I get is "operation not allowed in this context". This is because
when the content type is a text type then the ADODB stream object returned
by DecodedContentStream is in text mode and therefore is expecting us to use
WriteText not Write. This allows the stream object to encode the text to
the character set being used by the message.

I missed it because I simplified my code a little too much in my example.
The actual code I use (which is a VB6 dll) is 'overloaded' and in addition
to an array of bytes it can also take a refererence to IStream. In the
latter case the ADODB stream object's default interface is by passed and its
IStream interface is used to copy from the input IStream. This also
bypasses the character encoding that ADODB stream would have wanted to do
for a text type but since the transfer encoding is base64 this doesn't cause
a problem in transfer and the resulting file extracted at the other end is
byte for byte identical to the source.

If the email charset is different from the charset used in the source file
this might be a problem when the client tries to view the attachment.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top