从Excel 2019中的url下载文件(它在Excel 2007上工作)

irtuqstp  于 2023-03-31  发布在  其他
关注(0)|答案(7)|浏览(209)

我得到了一个代码,从一个需要凭据的网站下载CSV文件。我得到了一个代码,感谢这个网站,我可以适应我的需要。我的代码的相关部分是:

Option Explicit

Private Declare Function URLDownloadToFileA Lib "urlmon" _
    (ByVal pCaller As Long, _
    ByVal szURL As String, _
    ByVal szFileName As String, _
    ByVal dwReserved As Long, _
    ByVal lpfnCB As Long) As Long

Private Function DownloadUrlFile(URL As String, LocalFilename As String) As Boolean
    Dim RetVal As Long
    RetVal = URLDownloadToFileA(0, URL, LocalFilename, 0, 0)
    If RetVal = 0 Then DownloadUrlFile = True
End Function

Sub DESCARGAR_CSV_DATOS()

Dim EstaURL As String
Dim EsteCSV As String

EstaURL = "https://user:token@www.privatewebsite.com/export/targetfile.csv"
EsteCSV = "MyCSV " & Format(Date, "dd-mm-yyyy") & ".csv"

    With Application
        .DisplayAlerts = False
        .ScreenUpdating = False
    End With

    DownloadUrlFile EstaURL, _
        ThisWorkbook.Path & "\" & EsteCSV

    DoEvents

    Workbooks.Open ThisWorkbook.Path & "\" & EsteCSV, , True, , , , , , , , , , , True

    'rest is just doing operations and calculations inside workbook

End Sub

对不起,但我不能提供真实的的URL。无论如何,这段代码自2019年9月以来一直完美地工作。而且它每天都在完美地工作。
执行此代码的计算机都是Windows 7和Excel 2007和64位。没有一台失败。
但现在,这项任务要外包给另一个办公室,在那里,电脑是Excel 2019,Windows 10和64位。
代码在那里不起作用,没有出现任何错误,但函数DownloadUrlFile在Excel 2019 + W10上没有下载任何文件
所以恢复,Excel 2007 + Windows 7完美工作(今天测试)。Excel 2019 + Windows 10不工作(屏幕上没有错误)。
我试着去弥补的事情:
1.我已经检查了文件urlmon.dll是否存在于system32中,它确实存在
1.我尝试使用PtrSafe声明函数URLDownloadToFileA
1.如果我在装有Excel 2019 + W10的PC中手动输入Chrome中的URL,则文件会正确下载,因此URL是可以的。
这些都没有解决我的问题。我很确定解决方案真的很简单,因为代码在Excel 2007中运行得很好,但我找不到我在这里遗漏的东西。
我想得到一个在任何情况下都能工作的代码,但如果这是唯一的方法,我也会接受一个只在Excel 2019和Windows 10中工作的解决方案。
希望有人能对此提出一些看法。提前感谢。

UPDATE:也尝试了this post中的解决方案,但仍然没有任何结果。
**更新2:**此外,使用Excel 2010测试了这里发布的代码(Excel 2007),它工作得很好。
**UPDATE 3:**变量RetVal存储下载的结果。我知道一些值:

' Returns 0 if success, error code if not.
' Error codes:
' -2146697210 "file not found".
' -2146697211 "domain not found".
' -2147467260 "transfer aborted".

但在我的例子中,它返回-2147221020,那会是什么呢?

**更新4:**好吧,这只是奇怪。我已经尝试了相同的代码从公共网站下载不同的文件,它在Excel 2019 + W10中工作。我做了一个新的简单代码,如下所示:

#If VBA7 And Win64 Then
    Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
      Alias "URLDownloadToFileA" ( _
        ByVal pCaller As LongPtr, _
        ByVal szURL As String, _
        ByVal szFileName As String, _
        ByVal dwReserved As LongPtr, _
        ByVal lpfnCB As LongPtr _
      ) As Long
#Else
    Private Declare Function URLDownloadToFile Lib "urlmon" _
      Alias "URLDownloadToFileA" ( _
        ByVal pCaller As Long, _
        ByVal szURL As String, _
        ByVal szFileName As String, _
        ByVal dwReserved As Long, _
        ByVal lpfnCB As Long _
      ) As Long
#End If

Sub Descarga()
Dim EstaURL As String
Dim EsteCSV As String

EstaURL = privateone 'can't be shared, sorry
EsteCSV = "CSV Datos " & Format(Date, "dd-mm-yyyy") & ".csv"

    On Error GoTo Errores
    URLDownloadToFile 0, "https://tutorialesexcel.com/wp-content/uploads/2018/10/funciones.xlsm", ThisWorkbook.Path & "\" & EsteCSV, 0, 0
    URLDownloadToFile 0, EstaURL, ThisWorkbook.Path & "\" & EsteCSV, 0, 0
    Exit Sub
Errores:
    'Si es un bucle lo mejor sería no mostrar ningún mensaje
    MsgBox "Not downloaded", vbCritical, "Errores"
End Sub

显示URLDownloadToFile 0, "https://tutorialesexcel.com/wp-content/uploads/2018/10/funciones.xlsm", ThisWorkbook.Path & "\" & EsteCSV, 0,的行可以完美运行并下载文件。
URLDownloadToFile 0, EstaURL, ThisWorkbook.Path & "\" & EsteCSV, 0, 0不起作用。
因此,再次测试了完全相同的代码,但在Excel 2007和他们都工作
为什么第一个下载可以在Excel 2019 + W10上工作,而第二个不能在Excel 2007+W7上工作?

**UPDATE 5:**该URL是私有的,因为它包含用户名和密码,但它是这样的:

https://user:token@www.privatewebsite.com/export/target%20file.csv
感谢@Stachu,该URL在任何PC上的Internet Explorer上都无法手动工作(我的意思是在资源管理器导航栏中复制/粘贴)。该URL在所有PC上的Google Chrome中都可以完美工作。
这真的很奇怪,手动,在Internet Explorer上的URL不工作,但与VBA编码和Excel2007/2010上执行相同的URL完美的作品.也许我应该改变一些关于编码?

**更新6:**还在研究你的所有帖子。问题是我只是一个数据分析师,所以这里发布的大量信息对我来说听起来真的很难。

一天前我已经把所有的信息通过电子邮件发给了IT人员,现在还在等待答复。
与此同时,基于这里的信息,终于编写了一个完全不同的东西,适用于所有机器。它适用于Windows 7和10,Excel 2007和2010(安装为32位)和Excel 2019(安装为64位)。
我在这里添加代码,所以也许有人可以解释为什么它工作正常,但看起来问题是base64编码。
我现在得到的代码是这样的(添加了对Microsoft Winhttp Setvices 5.1的引用)

Application.ScreenUpdating = False

Dim whr As WinHttp.WinHttpRequest
Dim oStream As Object
Dim EsteCSV As String
Dim EstaURL As String

EstaURL = "https://user:pass@www.privatewebsite.com/export/target%20file.csv" 
EsteCSV = "CSV Datos" & Format(Date, "dd-mm-yyyy") & ".csv"

'Set whr = CreateObject("WinHttp.WinHttpRequest.5.1")

Set whr = New WinHttp.WinHttpRequest

whr.Open "GET", EstaURL, True
whr.setRequestHeader "Authorization", "Basic " & EncodeBase64("user" & ":" & "password")
whr.send

' Using 'true' above and the call below allows the script to remain responsive.
whr.waitForResponse
DoEvents

Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write whr.responseBody
oStream.SaveToFile ThisWorkbook.Path & "\" & EsteCSV
oStream.Close
DoEvents

Set oStream = Nothing
whr.abort
Set whr = Nothing
'rest of code for operations

Kill ThisWorkbook.Path & "\" & EsteCSV

Application.ScreenUpdating = True

End Sub

Private Function EncodeBase64(text As String) As String
  Dim arrData() As Byte
  arrData = StrConv(text, vbFromUnicode)

  Dim objXML As Object
  Dim objNode As Object

  Set objXML = CreateObject("MSXML2.DOMDocument")
  Set objNode = objXML.createElement("b64")

  objNode.DataType = "bin.base64"
  objNode.nodeTypedValue = arrData
  EncodeBase64 = objNode.text

  Set objNode = Nothing
  Set objXML = Nothing
End Function
3bygqnnd

3bygqnnd1#

子代码是好的。检查工具菜单中的引用在vba和作出声明ptrsafe如下

Private Declare PtrSafe Function URLDownloadToFileA Lib "urlmon" _

t3psigkw

t3psigkw2#

执行此代码的计算机都是Windows 7和Excel 2007和64位。没有一台失败。
但现在,这项任务要外包给另一个办公室,在那里,电脑是Excel 2019,Windows 10和64位。
代码在那里不起作用,没有出现任何错误,但是函数DownloadUrlFile在Excel 2019 + W10上没有下载任何文件
我猜在另一间办公室也不行。

只有当URL为私有且IP未加入白名单时才会出现这种情况,您可以咨询您的网络团队,他们是否已将该URL的IP加入白名单。

URLDownloadToFile 0,“https://tutorialesexcel.com/wp-content/uploads/2018/10/funciones.xlsm“,ThisWorkbook.Path &“\”& EsteCSV,0这一行可以完美地工作并下载该文件。
行URLDownloadToFile 0,EstaURL,ThisWorkbook.Path &“\”& EsteCSV,0,0不起作用。
因此,再次测试完全相同的代码,但在Excel2007和他们都工作
为什么第一个下载可以在Excel 2019 + W10上工作,而第二个不能在Excel 2007+W7上工作?
此外,同样的代码对公共URL工作得很好,而对私有URL却没有,除非有IP限制。

cidc1ykv

cidc1ykv3#

至于你的错误,-2147221020 =〉0x 800401 E4根据VBA Error Codes and Descriptions,这个错误是MK_E_SYNTAX,它是'无效的名字语法'。
当它说名字,我猜这意味着你的网址,老实说,网址看起来语法不正确...
"https://user:token@www.privatewebsite.com/export/targetfile.csv"
我不得不四处挖掘,看看这是否真的符合网址的网络标准。同时,我建议找出一个不同的网址。它可能是一个升级到urlmon.dll现在抱怨的网址,而Windows 7版本没有。
好的,我的错,实际上看起来你可以做这样的URI,理论上,所以我有一个URI片段
first-client:noonewilleverguess@localhost:8080/oauth/token取自OAuth2 Boot
好吧,所以它是有效的,重新顶部的第17页rfc3986
authority = [ userinfo "@" ] host [ ":" port ]
看起来你必须进入API调用来设置用户名和密码。

Option Explicit

'* with thanks to http://www.holmessoft.co.uk/homepage/WininetVB.htm

Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" ( _
    ByVal lpszAgent As String, _
    ByVal dwAccessType As Long, _
    ByVal lpszProxyName As String, _
    ByVal lpszProxyBypass As String, _
    ByVal dwFlags As Long) As Long

Private Enum InternetOpenAccessTypes
    INTERNET_OPEN_TYPE_PRECONFIG = 0 'Retrieves the proxy or direct configuration from the registry.
    INTERNET_OPEN_TYPE_DIRECT = 1 'Resolves all host names locally.
    INTERNET_OPEN_TYPE_PROXY = 3 'Passes requests to the proxy unless a proxy bypass list is supplied and the name to be resolved bypasses the proxy. In this case, the function uses INTERNET_OPEN_TYPE_DIRECT.
    INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4 'Retrieves the proxy or direct configuration from the registry and prevents the use of a startup Microsoft JScript or Internet Setup (INS) file.
End Enum

Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" ( _
    ByVal hInternetSession As Long, _
    ByVal lpszServerName As String, _
    ByVal nServerPort As Integer, _
    ByVal lpszUsername As String, _
    ByVal lpszPassword As String, _
    ByVal dwService As Long, _
    ByVal dwFlags As Long, _
    ByVal dwContext As Long) As Long

Private Const INTERNET_SERVICE_HTTP = 3

Private Const INTERNET_FLAG_NO_COOKIES = &H80000
Private Const INTERNET_FLAG_NO_CACHE_WRITE = &H4000000

Private Declare Function HttpOpenRequest Lib "wininet.dll" Alias "HttpOpenRequestA" ( _
    ByVal hHttpSession As Long, _
    ByVal lpszVerb As String, _
    ByVal lpszObjectName As String, _
    ByVal lpszVersion As String, _
    ByVal lpszReferer As String, _
    ByVal lpszAcceptTypes As String, _
    ByVal dwFlags As Long, _
    ByVal dwContext As Long) As Long

Private Declare Function HttpSendRequest Lib "wininet.dll" Alias "HttpSendRequestA" ( _
    ByVal hHttpRequest As Long, _
    ByVal lpszHeaders As String, _
    ByVal dwHeadersLength As Long, _
    ByVal lpOptional As String, _
    ByVal dwOptionalLength As Long) As Boolean

Private Declare Function InternetReadFile Lib "wininet.dll" ( _
    ByVal hFile As Long, _
    ByVal lpBuffer As String, _
    ByVal dwNumberOfBytesToRead As Long, _
    ByRef lpNumberOfBytesRead As Long) As Boolean

Private Sub Test()
    Dim hInternet As Long
    hInternet = InternetOpen("Mozilla", INTERNET_OPEN_TYPE_DIRECT, vbNullString, vbNullString, 0)
    If hInternet = 0 Then
        Debug.Print "InternetOpen failed"
        GoTo SingleExit
    End If

    Dim sUSERNAME As String
    sUSERNAME = "foo"

    Dim sPASSWORD As String
    sPASSWORD = "bar"

    Dim hConnect As Long
    hConnect = InternetConnect(hInternet, "www.microsoft.com", 80, sUSERNAME, sPASSWORD, INTERNET_SERVICE_HTTP, 0, 0)
    If hConnect = 0 Then
        Debug.Print "InternetConnect failed"
        GoTo SingleExit
    End If

    Dim lFlags As Long
    Dim hRequest As Long

    lFlags = INTERNET_FLAG_NO_COOKIES
    lFlags = lFlags Or INTERNET_FLAG_NO_CACHE_WRITE

    hRequest = HttpOpenRequest(hConnect, "GET", "www.microsoft.com", "HTTP/1.0", vbNullString, vbNullString, lFlags, 0)

    Dim bRes As Boolean
    bRes = HttpSendRequest(hRequest, vbNullString, 0, vbNullString, 0)

    Dim strFile As String
    strFile = "downloadedfile.txt"

    Dim strBuffer As String * 1
    Dim strDir As String
    strDir = Dir(ThisWorkbook.Path & "\" & strFile)
    If Len(strDir) > 0 Then
        Kill ThisWorkbook.Path & "\" & strFile
    End If

    Dim iFile As Long
    iFile = FreeFile()
    Open ThisWorkbook.Path & "\" & strFile For Binary Access Write As iFile

    Do
        Dim lBytesRead As Long
        bRes = InternetReadFile(hRequest, strBuffer, Len(strBuffer), lBytesRead)
        If lBytesRead > 0 Then
            Put iFile, , strBuffer
        End If
    Loop While lBytesRead > 0

    Debug.Print "finished"
SingleExit:

End Sub

更新:祝贺你的解决方案,你邀请一个解释,也许看到这个MSDN Forum,其中的话语概述了不同的技术栈。如果我浏览C++头文件urlmon.h,那么URLDownloadToFile看起来像是基于WinInet的。所以切换到WinHTTP是一个明智的举动,更多的基于服务器的堆栈。
此外,在相同的堆栈逻辑上,我相信您可以使用MSXML2.ServerXMLHTTP(参见VBScript newsgroup archive

efzxgjgh

efzxgjgh4#

我在Excel 2019 / O365 64位(版本:1912)/ win 10 64位
我知道你有一个工作守则,但如果其他人需要一个替代品,这里是:

Sub DownloadFile()

    Dim evalURL As String
    Dim streamObject As Object
    Dim winHttpRequest As Object
    Set winHttpRequest = CreateObject("Microsoft.XMLHTTP")

    evalURL = "https://fullPathTofile/tst.csv" ' -> Didn't need to add the username at the beginning

    winHttpRequest.Open "GET", evalURL, False, "username", "password"
    winHttpRequest.send

    If winHttpRequest.Status = 200 Then
        Set streamObject = CreateObject("ADODB.Stream")
        streamObject.Open
        streamObject.Type = 1
        streamObject.Write winHttpRequest.responseBody
        streamObject.SaveToFile "C:\temp\file.csv", 2 ' 1 = no overwrite, 2 = overwrite
        streamObject.Close
    End If

End Sub
qybjjes1

qybjjes15#

您使用的“简化”方法(user+password@url)由于其潜在的远程滥用,充其量只能得到零星的支持。
例如,...admin:admin@192.168.1.1/cgi-bin/something-else?...的HREF链路足以利用仅由“拒绝远程访问”默认值而不是可靠密码保护的几个路由器,并且有很多这样的路由器。
可能能够通过在Internet Explorer中保存用户名和密码来克服这个问题,Excel使用Internet Explorer的库,和/或将远程站点放在Internet选项中的“受信任的站点”组中。但这也是一种权宜之计,因为密码缓存可能会被意外删除,安全级别可能会在任何时候被更新重置(我不止一次遇到这种情况)。
Here还有其他讨论的方法。否则,你的解决方案当然有效(你可能想要添加一个答案来达到这个效果,并将其标记为接受,以备下一个遇到同样问题的人使用)。

gcuhipw9

gcuhipw96#

我为此挣扎了好几天,直到我发现它可以在PowerShell的一行中完成,例如:

invoke-webrequest -Uri "http://myserver/Reports/Pages/ReportViewer.aspx?%2fClients%2ftest&rs:Format=PDF&rs:ClearSession=true&CaseCode=12345678" -OutFile "C:\Temp\test.pdf" -UseDefaultCredentials

我想纯粹用VBA来做,但它运行了几页,让我失去了理智,所以每次我想下载文件时,我都从VBA调用PowerShell脚本。
非常,非常简单,“UseDefaultCredentials”绝对是一种享受,我不必担心登录到远程站点等。
这使我可以用几行代码将报告以PDF格式从SSRS下载到一个文件夹中。

33qvvth1

33qvvth17#

感谢大家的帮助和回答。不幸的是,我的IT部门无法告诉我到底发生了什么,即使这里提供了很多有用的信息。
我在这里发布我们现在正在使用的代码。它在Excel 2007 32位,Excel 2010 32和64位以及Excel 2019 64位上完美工作。它在Windows 7和10上也可以工作。
为了使这段代码工作,你需要添加一个对Microsoft Winhttp Setvices 5.1的引用。如果你不知道如何做,请检查How to Add an Object Library Reference in VBA

Application.ScreenUpdating = False

Dim whr As WinHttp.WinHttpRequest
Dim oStream As Object
Dim EsteCSV As String
Dim EstaURL As String

EstaURL = "https://user:pass@www.privatewebsite.com/export/target%20file.csv" 
EsteCSV = "CSV Datos" & Format(Date, "dd-mm-yyyy") & ".csv"

'Set whr = CreateObject("WinHttp.WinHttpRequest.5.1")

Set whr = New WinHttp.WinHttpRequest

whr.Open "GET", EstaURL, True
whr.setRequestHeader "Authorization", "Basic " & EncodeBase64("user" & ":" & "password")
whr.send

' Using 'true' above and the call below allows the script to remain responsive.
whr.waitForResponse
DoEvents

Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write whr.responseBody
oStream.SaveToFile ThisWorkbook.Path & "\" & EsteCSV
oStream.Close
DoEvents

Set oStream = Nothing
whr.abort
Set whr = Nothing
'rest of code for operations

Kill ThisWorkbook.Path & "\" & EsteCSV

Application.ScreenUpdating = True

End Sub

Private Function EncodeBase64(text As String) As String
  Dim arrData() As Byte
  arrData = StrConv(text, vbFromUnicode)

  Dim objXML As Object
  Dim objNode As Object

  Set objXML = CreateObject("MSXML2.DOMDocument")
  Set objNode = objXML.createElement("b64")

  objNode.DataType = "bin.base64"
  objNode.nodeTypedValue = arrData
  EncodeBase64 = objNode.text

  Set objNode = Nothing
  Set objXML = Nothing
End Function

再次感谢大家。这是一个很棒的地方。

相关问题