Adobe AIR/Flex file upload to a server that requires basic authentication - apache-flex

Adobe reference specifically points this out as a limitation.
Here's the note in the file.upload functionality in adobe reference.
Note: If your server requires user authentication, only SWF files running in a browser — that is, using the browser plug-in or ActiveX control — can provide a dialog box to prompt the user for a username and password for authentication, and only for downloads. For uploads using the plug-in or ActiveX control, or for uploads and downloads using the stand-alone or external player, the file transfer fails.
click here for the full reference page for File
Has anyone been able to work around this limitation. One thought I had was to use the native support in AIR for Javascript and see that works. Anyone tried that? Anyone have other ideas?
I've also tried the solution proposed here and it didn't work. According to the questioner in the forum it didn't seem to work for him either. Probably hitting the issue/limitation mentioned above.
I understand how to include basic authentication on a URLRequest and that works fine for me if I'm posting key/value pairs. But when I attempt to upload a file it does not work. It just sits there and does not fire any of the file.upload events. No errors no progress. Any help is much appreciated.
Here's my sample code:
var sendVars:URLVariables = new URLVariables();
sendVars.prop1 = "filename" ;
var urlRequest:URLRequest = new URLRequest();
urlRequest.method = URLRequestMethod.POST ;
urlRequest.data = sendVars ;
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("user:pass");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
urlRequest.requestHeaders.push(credsHeader);
file = new File(url);
file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA , file_UploadCompleteDataHandler );
file.addEventListener( Event.OPEN, fileOpenHandler);
file.addEventListener(ProgressEvent.PROGRESS, fileProgressHandler);
file.addEventListener(Event.COMPLETE, file_CompleteHandler);
file.addEventListener(IOErrorEvent.IO_ERROR, file_IOErrorHandler);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS , file_HTTPStatusHandler);
file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, file_SecurityErrorHandler);
try {
// Start the file upload
file.upload(urlRequest, "primaryFile", false);
}
catch (error:Error) {
trace( "Unable to upload file." + file.name);
}

Do exactly what you are doing except get the file.data and pass it into this....
public static function prepareWithFormData(vars:MetadataList, bytes:ByteArray, fieldName:String, filename:String, request:URLRequest):void {
bytes.position = 0;
var boundary: String = '---------------------------' + UIDUtil.createUID();
var header1: String = "";
var varsArray:Array = vars.asArray();
for each(var item:Metadata in varsArray) {
header1 += "\r\n--" + boundary + "\r\n";
header1 += 'Content-Disposition: form-data; name="' + item.name + '"\r\n\r\n';
header1 += item.value;
}
header1 += '\r\n--'+boundary + '\r\n'
+'Content-Disposition: form-data; name="' + fieldName + '"; filename="'+filename+'"\r\n'
+'Content-Type: application/octet-stream\r\n\r\n'
//In a normal POST header, you'd find the image data here
var header2:String = '\r\n--'+boundary + '--';
//Encoding the two string parts of the header
var headerBytes1: ByteArray = new ByteArray();
headerBytes1.writeMultiByte(header1, "ascii");
var headerBytes2: ByteArray = new ByteArray();
headerBytes2.writeMultiByte(header2, "ascii");
//Creating one final ByteArray
var sendBytes: ByteArray = new ByteArray();
sendBytes.writeBytes(headerBytes1, 0, headerBytes1.length);
sendBytes.writeBytes(bytes, 0, bytes.length);
sendBytes.writeBytes(headerBytes2, 0, headerBytes2.length);
request.data = sendBytes;
request.method = URLRequestMethod.POST;
request.contentType = "multipart/form-data; boundary=" + boundary;
}

Related

Can't return word document, generated in memory stream, for downloading to user

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.

Empty file received using HTTP Post method

I'm trying to download a file using HTTP, and here is the code.
With this, I have a directory made with a correct name, and a file within the directory made with a correct name, but there is NOTHING WRITTEN in the file.
PostMethod post = new PostMethod(serverUrl);
post.setRequestEntity(entity);
httpclient.executeMethod(post);
File contentDirectory = new File(fileFullPath);
if(contentDirectory.exists() == false){
contentDirectory.mkdir();
}
File localFile = new File(fileFullPath + File.separator + filename);
int readBuf = 0;
byte[] buf = new byte[Utils.getBufferSize()]; (BufferSize Checked)
InputStream is = null;
is = post.getResponseBodyAsStream();
FileOutputStream fos = new FileOutputStream(localFile);
while((readBuf = is.read(buf))!= -1){
fos.write(buf, 0, readBuf);
logger.info("readBuf : "+readBuf);
}
is.close();
fos.close();enter code here
if(localFile.exists()) Transfer_Success = true;
Being a noob I am, turns out all this time I was sending post method to a wrong servlet. A mistake only novices make.
So I have the bytes transferred correctly, but this time the image files can't be open due to wrong encoding type or something. I'm on to resolving this.

Getting File Path not supported while trying to download multimedia image from Tridion 2011 SP1

I am getting "The given path's format is not supported." when I am just trying to download multimedia image from my SDL Tridion 2011 SP1, below is the path I am getting, no idea how "N:" etc are coming.
D:\delete\Images\N:\dmc.FlipMedia.Clients.TestCMS\2009_WorkInProgress\creatives\05_May\Kids under 16 go free to UK\assets_graphics\jpg\Kids_go_free_385x306.jpg
Below is the code:
public static void GetBinaryFromMultimediaComponent(string tcm, CoreServiceClient client, StreamDownloadServiceClient streamDownloadClient)
{
ComponentData multimediaComponent = client.ReadItem(tcm) as ComponentData;
// Generate you own file name, and file location
string file = "D:\\delete\\Images\\" + multimediaComponent.BinaryContent.Filename;//Here I am getting above path
// Write out the existing file from Tridion
FileStream fs = File.Create(file);//Here getting the exception
byte[] binaryContent = null;
if (multimediaComponent.BinaryContent.FileSize != -1)
{
Stream tempStream = streamDownloadClient.DownloadBinaryContent(tcm);
var memoryStream = new MemoryStream();
tempStream.CopyTo(memoryStream);
binaryContent = memoryStream.ToArray();
}
fs.Write(binaryContent, 0, binaryContent.Length);
fs.Close();
}
Please suggest!!
Edit:
I got filename using Nuno Suggestions, however moving forward to
Stream tempStream = streamDownloadClient.DownloadBinaryContent(tcm);
I am getting below error, any suggestion on this?
The content type multipart/related; type="application/xop+xml";start="";boundary="uuid:5f66d04b-76d3-4d3a-b8e3-b7b91e00ed32+id=2";start-info="text/xml" of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 595 bytes of the response were: ' --uuid:5f66d04b-76d3-4d3a-b8e3-b7b91e00ed32+id=2 Content-ID: Content-Transfer-Encoding: 8bit Content-Type: application/xop+xml;charset=utf-8;type="text/xml" '.
As you probably figured out by now, string file = "D:\\delete\\Images\\" + multimediaComponent.BinaryContent.Filename;will append full file name (including path) and therefore generate a wrong path.
Try using something like string file = "D:\\delete\\Images\\" + Path.GetFilename(multimediaComponent.BinaryContent.Filename);
Do this way:-
Stream tempStream = streamDownloadClient.DownloadBinaryContent(tcmId);
MemoryStream memoryStream = new MemoryStream();
int b;
do
{
b = tempStream.ReadByte();
memoryStream.WriteByte((byte)b);
} while (b != -1);
binaryContent = memoryStream.ToArray();​

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!

How can I send another variable with FileReference Upload?

I have a problem with the filereference class using the upload function.
I want to send the folderLocation variable with fileReference.upload().
Below I try to describe my strategy.
var folderLocation : String = "photos/myUniqueFolder/";
private var serverSideScript:String = "http://localhost/project/phpFlexMechanism/upload.php";
urlRequest = new URLRequest(serverSideScript);
fileReferenceList.addEventListener(Event.SELECT, fileSelectedHandler);
private function fileSelectedHandler(event:Event):void {
// upload the file to the server side script
fileReference.addEventListener(Event.COMPLETE, uploadCompleteHandler);
fileReference.upload(urlRequest);
}
In PHP I use this to get the the file and uploaded
$folder = $_POST['folder'];
$tempFile = $_FILES['Filedata']['tmp_name'];
$fileName = $_FILES['Filedata']['name'];
$fileSize = $_FILES['Filedata']['size'];
move_uploaded_file($tempFile, "../user/$folder/uploadImages/" . $fileName);
But how can I send the folder through the "upload reference"?
I would think that, since you FileReference needs a URLRequest, you would be able to piggy back that information through the URLRequest itself by using the flash.net.URLVariables object.
I've not had time to test this, but have you tried:
// put this right after you instantiate urlRequest;
var urlVars:URLVariables = new URLVariables();
urlVars.targetFolder = folderLocation;
urlRequest.method = "post";
urlRequest.data = urlVar;
That should let you do:
//replace your last line with these two.
$folder = $_REQUEST[ "targetFolder" ];
move_uploaded_file($tempFile, "../user/$folder/uploadImages/" . $fileName);
in PHP.
the easiest thing you can do is just send it through with a get header in the upload function
so the code looks as follows
private var serverSideScript:String = "http://localhost/project/phpFlexMechanism/upload.php?extraVar=value&extraVarB=value2";
fileReference.upload(urlRequest);
then in the php script you just use
$_GET['extraValue']
$_GET['valueVarB']

Resources