웹앱 다운로드 /웹앱에서 다운로드 구현 / 다운로드 매니저/ download manager in web app webapp / android webapp
android web app 에서 pdf download 기능
public link
만약 pdf 가 public link 로 되어 있다면 간단하다. webView 에 download listener 를 등록해주면 된다.
- browser - How to encode the filename parameter of Content-Disposition header in HTTP? - Stack Overflow: Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt 관련 내용
// MainActivity.kt
import android.Manifest
import android.app.Activity
import android.app.DownloadManager
import android.content.Context
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.webkit.*
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import com.mycomp.R
class MainActivity : Activity() {
val FILECHOOSER_REQ_CODE = 2001
private val webView by lazy { findViewById<WebView>(R.id.webview) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
bindViews()
...
}
var valueCallback: ValueCallback<Array<Uri>>? = null
private fun bindViews() {
with(webView) {
webViewClient = MyWebClient(this@MainActivity, deepLink)
webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
if (valueCallback != null) {
valueCallback?.onReceiveValue(null)
valueCallback = null
}
valueCallback = filePathCallback
checkPermission()
return true
}
}
settings.textZoom = 100
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
val cookieManager = CookieManager.getInstance()
cookieManager.setAcceptCookie(true)
cookieManager.setAcceptThirdPartyCookies(this@with, true)
}
// ------------------------------------------------------------------------
//
// download listener
//
// ------------------------------------------------------------------------
this@with.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
//checking Runtime permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
//Do this, if permission granted
downloadDialog(url, userAgent, contentDisposition, mimetype)
} else {
//Do this, if there is no permission
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
}
} else {
//Code for devices below API 23 or Marshmallow
downloadDialog(url, userAgent, contentDisposition, mimetype)
}
}
}
}
fun downloadDialog(url: String, userAgent: String, contentDisposition: String, mimetype: String) {
//getting file name from url
val filename = getFileName(url, contentDisposition, mimetype)
//Alertdialog
val builder = AlertDialog.Builder(this)
//title for AlertDialog
builder.setTitle("Download")
//message of AlertDialog
builder.setMessage("Do you want to save $filename")
//if YES button clicks
builder.setPositiveButton("Yes") { dialog, which ->
val urltoken = url.split("$$")
//DownloadManager.Request created with url.
val request = DownloadManager.Request(Uri.parse(urltoken[0]))
//cookie
val cookie = CookieManager.getInstance().getCookie(url)
//Add cookie and User-Agent to request
request.addRequestHeader("Cookie", cookie)
request.addRequestHeader("User-Agent", userAgent)
request.addRequestHeader("Authorization", "Bearer " + urltoken[1])
//file scanned by MediaScannar
request.allowScanningByMediaScanner()
//Download is visible and its progress, after completion too.
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//DownloadManager created
val downloadmanager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
//Saving file in Download folder
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
//download enqued
downloadmanager.enqueue(request)
}
//If Cancel button clicks
builder.setNegativeButton("Cancel")
{ dialog, which ->
//cancel the dialog if Cancel clicks
dialog.cancel()
}
val dialog: AlertDialog = builder.create()
//alertdialog shows
dialog.show()
}
private fun getFileName(url: String, contentDisposition: String, mimetype: String): String {
var filename = URLUtil.guessFileName(url, contentDisposition, mimetype)
// attachment; filename=_20210102_175545.xlsx; filename*=utf-8''%EC%82%AC%EC%9A%A9%EC%9E%90%EB%AA%A9%EB%A1%9D_20210102_175545.xlsx
// to handle the "filename*=..."
val regex = Regex("filename\\*=.*\\'\\'(.*)$", setOf(RegexOption.DOT_MATCHES_ALL))
val matchResult = regex.find(filename)
if(matchResult != null && matchResult.groups[1] != null){
filename = matchResult.groups[1]?.value ?: filename;
}
return filename
}
...
}
Auth 가 필요한 link
만약 auth 가 필요하다면 token 등을 GET parameter 로 넘기면 위의 publick link 를 사용하는 방법으로 처리할 수 있다.
그것이 아니라 만약 header 에 token 을 넣어야 하는 경우라면, app 이 token 을 가져올 방법이 있어야 한다. 또는 이것을 그냥 Blob 으로 받고, 이 Blob 으로 받은 것을 다시 저장하는 방법도 있다.
- GET parameter 로 token 을 전달
- Auth Header 에 넣는 경우
- js 에서 blob download 로 처리되게 하고, blob 을 base64 로 변환해서 다시 저장하는 방법
- token 을 app 으로 전달해서 header 에 token 을 채우는 방법
Custom URI schema
“token 을 app 으로 전달해서 header 에 token 을 채우는 방법” 을 사용했는데, 이를 위해서 URI scheme 을 설정했다. scheme 은 아래의 모양을 띤다.
mycomp://download?url=<pdf-url>&token=<mytoken>¶m1=paramval1
그리고 이 URI 를 click 할때의 action 에 대해서 override 를 했다. WebViewClient.shouldOverrideUrlLoading
을 override 하면 된다.
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
val intent = parse(url ?: "")
return when {
...
isMyCompURI(url) -> {
if(url != null) {
val (downloadUrl, token) = _parseURI(url)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
//Do this, if permission granted
createAndShowDownloadDialog(view, downloadUrl ?: "", token);
} else {
//Do this, if there is no permission
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
}
} else {
//Code for devices below API 23 or Marshmallow
createAndShowDownloadDialog(view, downloadUrl ?: "", token);
}
}
return true
}
else -> !(url?.startsWith("http://") == true || url?.startsWith("https://") == true)
}
}
private fun isMyCompURI(url: String?) = url?.matches(Regex("^mycomp://\\S+$")) ?: false
JavaScriptInterface
“js 에서 blob download 로 처리되게 하고, blob 을 base64 로 변환해서 다시 저장하는 방법” 은 아래 링크를 참고하자.
댓글 없음:
댓글 쓰기