Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Drag and Drop revisited 1

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
Hi All,
Some time ago I implemented drag and drop from browser to my VFP app. And it worked fairly well most of the time.
But recently I find more and more it's not working, and the issue is something to do with the image. I'm not clear on what the issue is.
For example, this image:


Fails when I drop it onto my image class on the form.

But this image:


From what I can tell the key difference is the name of the file is in the URL. So when I drag and drop the image, if it has a valid file name it works fine.

The first one fails, but if I drag and drop that image directly into a Windows Folder, Windows will give it a name (like "image" or "download" or "0" or something else). If I then drag and drop that image onto the control, it works fine.
What I'm seeking is, what is it that Windows folder can do with the drag and drop image that VFP isn't?

I'm fine if I can rename the file in the drop, but when I try to give it a name at the drop time, it fails with something like "file not available from URL" or something to that effect (I don't have the error on hand).

The two images above are just examples I found that break. They're not important on their own.
Is there some way I can "transform" the first dragged URL into some file that I can drop directly onto the form?
The OLEDragDrop Event code I have is:

Code:
LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord
LOCAL aValues, i, cText, nOperation
*
* Creates the proper directory structure to store images in, otherwise the storage routines pitch a fit and won't save.
lcDirectoryString = "\DOCS\CERTS\"+ADDBS(ALLTRIM(STR(GLOBALCERT.CERTID)))
*
IF NOT DIRECTORY(SYS(5)+SYS(2003)+lcDirectoryString)
	MD(SYS(5)+SYS(2003)+lcDirectoryString)
ENDIF
*
Thisform.LockScreen = .T.
DO CASE
CASE oDataObject.GetFormat(1)	&& Text - In this case from a URL
	cText = oDataObject.GetData(1)
*-- Add the text as a new item in the list
	This.AddItem(cText)
* Check to make sure the URL contains a file in it's expression.  This will assume that the ".Ext" will be 4 characters from the end.
	IF LEFT(RIGHT(cText,4),1) # "."
		MESSAGEBOX("This file can not be moved directly from web page.  Try saving it locally, and then adding with Right-Click",64)
		RETURN
	ENDIF
*
*	We ahve a good file from the URL, so let's build the destination saves (one for file, one to update the table).
*
		lcFileSave = SYS(5)+SYS(2003)+lcDirectoryString+ALLTRIM(GLOBALCERT.CERTCOMPANYNAME)+"-Cert"+RIGHT(cText,4)
		lcFieldSave = ADDBS(lcDirectoryString)+ALLTRIM(GLOBALCERT.CERTCOMPANYNAME)+"-Cert"+RIGHT(cText,4)
*
	lnSaveSuccess = STRTOFILE(ThisForm.oleInternet.OpenURL(cText,1),lcFileSave) && Leaving this here for now in case we switch methods back later.
*
	IF lnSaveSuccess > 0
		SELECT (ThisForm.ActiveDBF)
		REPLACE GLOBALCERT.CERTIMAGE WITH lcFieldSave
*
		IF NOT TABLEUPDATE()
			WAIT WINDOW "Unable to Save"
			TABLEREVERT()
		ENDIF
	ELSE
		MESSAGEBOX("There was an unexpected problem, probably with the URL which prevented the image from being saved.  "+;
			"Try saving it locally, and then adding with Right-Click", 64)
	ENDIF
*
CASE oDataObject.GetFormat(15)	&& Files CF_DROP
	DIMENSION aValues[1]
	oDataObject.GetData(15, @aValues )
	
	*-- Add each filename as a new item in the list
	FOR i = 1 to alen(aValues)
		This.AddItem(aValues[m.i])
	NEXT
*
	cText = aValues(1)
	lcFileSave = SYS(5)+SYS(2003)+lcDirectoryString+ALLTRIM(GLOBALCERT.CERTCOMPANYNAME)+"-Cert"+RIGHT(cText,4)
	lcFieldSave = ADDBS(lcDirectoryString)+ALLTRIM(GLOBALCERT.CERTCOMPANYNAME)+"-Cert"+RIGHT(cText,4)
*
	llStore = .F.
	TRY
		COPY FILE (cText) TO (lcFileSave)
		llStore = .T.
	CATCH
		* something went wrong in copying the file. llStore is still .F.
	ENDTRY
*
	IF llStore
		SELECT (ThisForm.ActiveDBF)
		REPLACE GLOBALCERT.CERTIMAGE WITH lcFieldSave
*
		IF NOT TABLEUPDATE()
			WAIT WINDOW "Unable to Save"
			TABLEREVERT()
		ENDIF
	ELSE
		MESSAGEBOX("There was an undisclosed problem which prevented the image from being saved.  Please retry later.")
	ENDIF
ENDCASE
* ThisForm.Refresh()
Thisform.LockScreen = .F.
*
The message boxes are just to alert that the drop of the image didn't work. I've built in on the right click the GETFILE() function, I drag the image to that, and then pick it in place, and then it adds it, but this is a lot of annoying steps for my users. They want to just drag and drop and be done with it.
Any ideas how to resolve this? The "internet" was not really present in the height of my FPW 2.6 days, so none of this existed, and I don't fully understand it still.
Thanks.


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Not sure if this will help, Scott, but there's an obvious difference in the URLs of the two examples you gave. The second one (the one that you say works fine) contains the actual filename of the image, that is, of the JPG. The first one contains what I assume is the name of script file which receives a parameter. The parameter presumably tells the script which image you want to display. The drag-and-drop operation can only work with the actual file, and has no way of interpreting the parameter.

If that's correct, then the solution is to specify the actual filename in the URL. Presumably the file physically exists on the server. You would have to figure out how to determine what that filename is, and to programmatically adjust the URL accordingly.

All this is a bit of a guess on my part - just based on the difference in the URL.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,
I agree 100% with your assessment. It's that second part that I'm trying to "solve". And I'm bewildered by how Windows is able to do it... So if I drag and drop the image from the first URL into a folder in Windows, it will assign the filename "images.jpg", but I don't understand how it does that. To your point, the STRTOFILE() Fails because there is no file name. If I add on something like \image.jpg to the URL it fails as well, saying there's no such file.
While the URL with the file name in it works just fine.

So I'm looking for a trick that will allow me to convert that obscure URL into a way of providing a file name. Because I can do it in any windows folder, I assume, there must be some way to get VFP to recognize it, but it's in that "internety realm" that I don't really fathom so well.

Any ideas?


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
I agree with Mike, you mainly just have the too strict condition the URL has to end in the file type extension.

You get the file type within the HTTP response header, not sure whether you can get at it with the OLE InternetTranfer Control you use, but you can get the head of an HTTP response with a HEAD request:
Code:
Local o as msxml2.xmlhttp
o = CREATEOBJECT("msxml2.xmlhttp")
o.open("HEAD","[URL unfurl="true"]https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT8vWuJsSFaFvy7d6DwdBmcRHDsc6dDrS1u35atJgVEhsGmPioj")[/URL]
o.send()
DO WHILE o.readyState<4
   DOEVENTS force
ENDDO 
? o.getResponseHeader('Content-Type')
Reveals the type is image/jpeg.

The Windows Explorer doesn't manage more than you, it just simply takes whatever comes and as you see doesn't care about a file extension. By the way if I drag the first link Chrome doesn't create an image, but a ul file just containing the URL. If you simply do what you also do for urls with 3 letter file extensions (by the way it could also be 4 letter like .jpeg is) you simply get the file.

A wonder only would be, if you get a preview image from the core image file without extension. If I rename a file to remove file extension, I'm prompted to choose a program, picking MS paint it works and displays the pic, as it can find enough information in the file itself, I don't even know an image format not starting with a chunk of bytes telling what it is. So that works that way. No magic involved.

If you simply comment the rejection of urls without file extension you get the same files without file extension:
Code:
*	IF LEFT(RIGHT(cText,4),1) # "."
*		MESSAGEBOX("This file can not be moved directly from web page.  Try saving it locally, and then adding with Right-Click",64)
*		RETURN
*	ENDIF

You're standing in your own way with this, that's not an incapability of the ole internet transfer control. Your code should just not attach Right(cText,4) as file extension but ideally determine that from the HEAD of the response body, from the Content-Type header.

You might have a look into this: thread184-1765795

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Olaf,
Have read through the other post link, and your response here. I've made some changes, it's still broken, but I see your point about my standing in my own way. This is VERY old code from about 3 years ago now, when I was just creeping back into VFP. The example came straight out of the SAMPLES directory for drag and drop and the STRTOFILE with OLEInternet was something you helped me with initially. For a couple of years it worked for most (80%+) things, but the net has changed and I found having to use the rightclick, drag and save into a GETFILE() dialog, and then add it that way, but it has become cumbersome as now 50%+ of my dragged images started to fail.

I'm going to tinker around a bit more with this, but from what you've shown so far, and the other examples from Dennis' post, I'm pretty sure I can get it working, and that will be awesome. Haven't played with (or even knew about) HEAD and that msxml2.xmlhttp class, but saw your other conversation with Dennis on it, and I just need to rework my OLEDragDrops, and this should work great.
Will come back after I either have it working, or have broken everything. ><


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Olaf,
You have provided some real genius to me (and lots of others here) over the last few years, but this may well be the crowning achievement. That trick with URLDownloadToFile coupled with the HEADer data is amazing. It's exactly what I've been trying to solve for a couple of years. If I had a 100 years I would not have figured this out on my own. Can't thank you enough. Wish there was a 10 star button.

I'm updating my subclass now so this will populate across all my image objects. Just need to work out a few things so that the files store to the locations that I want them in.

Many Many thanks for this.


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Thanks, I also learned this from others and elsewhere, so I can virtually pass this on to several others.

You could also determine the file type from the first few bytes of the download, I remember I posted about the inverse problem, looking for certain image file header signatures in an EXE or APP file to extract images. There's a thread here Mike Lewis also put on his website:
The thread here on tek-tips didn't end with just this: thread184-1732378.

Overall you find some gems if you simply click on the star in the top bar of the forum overview and thread listing under "thread order". Brings up all posts in order of number of stars given.

Bye, Olaf.

Olaf Doschke Software Engineering
 
I noticed that some headers indicate the type is "HTML", but the image control (I assume) doesn't care about the extension, if it can display the contents it does, as I found by just letting it write out. Which was something I think you were explaining before. But now I see it in action. I really don't care about extensions, so long as the image displays as desired.
Thanks for the other links, I'll give them a look as well.
This is a really useful function, and I'm using it not only to get an image, but specifically for types of images I'm getting, they have text in them, and then I OCR the text from the image, store the image for viewing, but populate fields in the form (table) along with it. Has made getting some data and updating it really easy.
I've still got a couple more minor things to work out, but the core is now working, and I have implemented the OCR on either drag and drop, or file selection via GETFILE() dialog.
Can't tell you how much I love this. Has taken me two weeks to build this new section of our enterprise application but it has going to make a really good research area in or app once we get the data populated. Now populating it is dragging one string of text (city name) and one image (a certification with data in the image), and it populates the entire entry. Love it!


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott - I'm curious how you solved this. I'm putting together a session on Drag-and-Drop for Southwest Fox and this is an interesting problem.

What I'm specifically interested in is which method worked for you in getting from an URL that isn't just a filename to a file you could display.

Tamar
 
Tamar,

of course, only Scott can answer, whether the HEAD request gave him enough information in general or whether he did do more than that, eg inspection of the file header itself. At least these are the two options you have.

A script can of course also report a wrong content-type header, that doesn't correspond to the real file content, as happens with the PHP script just posted yesterday at thread434-1789261. In general, when the URL really is just a file 1:1 it's mime type is consistent with what you get, so the only problematic cases could be script generated files with wrong content-type. The script shows another header browsers take to suggest a file name, the Content-Disposition.

The mime types for images are almost all like "image/fileextension", you can find an overview of them.

You can also make the full GET request instead of just HEAD, the response body would then contain the file and the Content-Type header will still give you the hint which file extension to apply when saving the responseBody:
Code:
Local o as msxml2.xmlhttp
o = CREATEOBJECT("msxml2.xmlhttp")
o.open("GET","[URL unfurl="true"]https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT8vWuJsSFaFvy7d6DwdBmcRHDsc6dDrS1u35atJgVEhsGmPioj")[/URL]
o.send()
DO WHILE o.readyState<4
   DOEVENTS force
ENDDO 
? o.getResponseHeader('Content-Type') 
lcFilename = ForceExt(Addbs(GetEnv("TEMP"))+"downloaded",getFileExtension(o.getResponseHeader('Content-Type')))
StrToFile(o.responseBody, lcFilename)
? lcFilename

Procedure getfileextension()
   Lparameters tcMimetype
   Local lcType, lcExact
   
   lcExact = SET('EXACT')
   SET EXACT OFF
   If tcMimetype="image/" && SET EXACT OFF
      lcType = Substr(tcMimetype,7)
   Else
      * extend this as far as necessary
      lcType = "unknown"
   EndIf    
   SET EXACT &lcExact
   
   Return lcType
EndProc

Then I pointed out Mike Lewis publishing of the image extraction from an APP/EXE and in that code is some detection of image types via inspection of the file itself. You know the thread I linked to already, as you yourself suggested that could be a FoxRockx article.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Olaf - thanks. I've played with this and while it works in some cases, I can't make it worth in others. Specifically, I can't get anything in Google Photos or from a Google image search to work.

I've talked to Scott in email and Google images work for him with similar code, so I'm fairly baffled.

For now, I think this doesn't go in the session because I just don't have more time to spend figuring it out.

Tamar
 
It sure has limits, for example as download links for google drive are not for a file but a webpage. Not sure why it would not work in Google image search. Does IE log in to your Google profile, if you use it? msxml2.xmlhttp is bound to IE browser settings including such authentication, that might make a difference there vs Chrome or Firefox if you have them or another standard browser.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Olaf and Tamar,
Interesting. I had been using Chrome browser, and the drag and drop on some images fails. I hadn't thought about it being a browser issue. But if I go to IE, then it works...
BUT, still one oddity. The .JPG in some cases if copied, and I can see it in the directory, but the image object won't display the content.
And weirder, I tried to open in Photoshop, but it tells me it can't parse the JPG file, but if I just double click the image in the folder (where the thumbnail is correct), Windows Photo Browser will display the image corectly. So something is very odd about certain files.
Is there some way in VFP to inspect a file and determine it's type (ignoring extension)?


Best Regards,
Scott
MIET, MASHRAE, CDCP, CDCS, CDCE, CTDC, CTIA, ATS

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Is there some way in VFP to inspect a file and determine it's type (ignoring extension)?

There are several utilities around that will do that, so in theory it should be possible to do it in VFP as well. As far as I can make out, the utilities all rely on a list of known "signatures" that enable them to recognise the file type. If you could get you hands on such a list - or, at least, find out what the signatures are for JPGs, etc - you should be on your way to solving the problem.

Examples of a couple of utilities: TrID and ExifTool. Both of those have a command-line interface, so it might be possible to automate the process from within VFP.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
VFP relies on gdiplus.dll to display images and in some sense participates in any new types of JPG JFIF or any such images running under one file extension, but I faintly remember images, which didn't display in an image control but in other software. There was some fix about PNG alpha transparency in VFP9 SP2, so some of the image file processing and rendering is not just outsourced to GDIPlus.dll, it's more of an OS concern than VFP, though. It could help to use some of the tools Mike mentions to further analyze files. Could be they turn out to be a single image video file, too, you get the weirdest combinations of file content that just works in some and not all software.

Chrome isn't used by the latest code I posted, but in conjunction with your drag&drop actions you also get a URI from Chrome pasted into VFP and can do the HEAD request via the COM XmlHTTPRrequest ("msxml2.xmlhttp and some others, too) object which roots in the IE world will not know the authentications you do with Chrome, so it just can do HEAD requests to files or URIs, that are public and don't require any authentication process to see them. I would guess the internet transfer control is rooted in the same IE surrounding environment and would also only download a URI that's reachable for it. Using IE (not Edge, really 32bit IE) you can also profit from the browser cache with both msxml2.xmlhttp and the internettransfer control.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Guess I need to add a few more comments. I tried Scott's code, which is similar to Olaf's on two different machines (Win 7 and Win 10) and four different browsers (Firefox, Chrome--only on Win 10 machine, Edge--only on Win 10 machine) and IE (only on Win 7). IIRC, the file was created in all cases except using IE, but the file was useless. Can't see it in Explorer, and depending what I use to open it, I get some kind of error message that boils down to Invalid image. With IE, I think I wasn't even able to create a file.

I tried Olaf's code only on the Win 7 machines and only with FireFox, I think.

I was logged into Google probably in all cases because I was trying this with my own photos stored in Google photos.

I'm pretty sure I have enough material for the session without this, so I'm not stressing.

Tamar

 
Tamar Granor said:
I was logged into Google probably in all cases because I was trying this with my own photos stored in Google photos.

Well, if you use Google Photos with Firefox and then use msxml2.xmlhttp or the internet transfer control, these are not seeing your firefox session. Cookies and cache are not an OS level thing but per browser platform and so the usage of the browser is important.

The way I save the file with these two lines is configurable as you like
Code:
lcFilename = ForceExt(Addbs(GetEnv("TEMP"))+"downloaded",getFileExtension(o.getResponseHeader('Content-Type')))
StrToFile(o.responseBody, lcFilename)

The only problem I have with google photo image URLs is they are too long for a string literal and the usage of & within the string (URL parameters) triggers VFP macros substitution, the drag&drop action should help passing that in as is. You can also "copy image location" putting the image URL into _cliptext, so you can start the GET request with:

Code:
o.open("GET",_cliptext)

I don't know now, if you rather have problems with drag & drop changing the URL or string literals or what else. I know it's not important for your session, but it's really quite simple to get this going without drag&drop first, and then build up from there.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top