入门文章

Blazor Hybrid / MAUI 简介和实战

https://www.cnblogs.com/densen2014/p/16240966.html

在 Mac 上开发 .NET MAUI

https://www.cnblogs.com/densen2014/p/16057571.html

在 Windows 上开发 .NET MAUI

https://docs.microsoft.com/zh-cn/dotnet/maui/get-started/installation

之前的工程已经能正常使用blazor的webview下获取定位,使用相机等功能,新版释出后反而权限获取不到了,定位页面出现如下错误

由于这个问题主要出现在安卓系统,下面只选了安卓的步骤分享

Android

应用所需的权限和功能在 AndroidManifest.xml 中定义。请参阅 官方文档 了解 Android App Manifest。

某些 Android 设备权限需要在运行时显示提示,以便用户可以授予或拒绝该权限。 Android 有一个推荐的 workflow 用于在运行时请求权限,此工作流必须由应用手动实现。 WebView 的 WebChromeClient 负责对权限请求做出反应,因此该项目提供了一个 PermissionManagingBlazorWebChromeClient 将 Webkit 资源映射到 Android 权限并执行推荐的权限请求工作流。

在向 AndroidManifest.xml 添加其他权限后,请务必更新 PermissionManagingBlazorWebChromeClient.cs 以包含该权限的“基本原理字符串”,解释应用程序需要它的原因。可能还需要在 权限请求类型 和 Android Manifest 权限之间定义其他映射。

1. 应用所需的权限Platforms/Android/AndroidManifest.xml

以下是我所有的测试权限列表,各位看官按需自由组合.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ModifyAudioSettings" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.CaptureSecureVideoOutput" />
<uses-permission android:name="android.permission.CaptureVideoOutput" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>

2. 添加文件 Platforms/Android/PermissionManagingBlazorWebChromeClient.cs

using Android;
using Android.App;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Views;
using Android.Webkit;
using AndroidX.Activity;
using AndroidX.Activity.Result;
using AndroidX.Activity.Result.Contract;
using AndroidX.Core.Content;
using Java.Interop;
using System;
using System.Collections.Generic;
using View = Android.Views.View;
using WebView = Android.Webkit.WebView; namespace BlazorMaui; internal class PermissionManagingBlazorWebChromeClient : WebChromeClient, IActivityResultCallback
{
// This class implements a permission requesting workflow that matches workflow recommended
// by the official Android developer documentation.
// See: https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions
// The current implementation supports location, camera, and microphone permissions. To add your own,
// update the s_rationalesByPermission dictionary to include your rationale for requiring the permission.
// If necessary, you may need to also update s_requiredPermissionsByWebkitResource to define how a specific
// Webkit resource maps to an Android permission. // In a real app, you would probably use more convincing rationales tailored toward what your app does.
private const string CameraAccessRationale = "This app requires access to your camera. Please grant access to your camera when requested.";
private const string LocationAccessRationale = "This app requires access to your location. Please grant access to your precise location when requested.";
private const string MicrophoneAccessRationale = "This app requires access to your microphone. Please grant access to your microphone when requested."; private static readonly Dictionary<string, string> s_rationalesByPermission = new()
{
[Manifest.Permission.Camera] = CameraAccessRationale,
[Manifest.Permission.AccessFineLocation] = LocationAccessRationale,
[Manifest.Permission.RecordAudio] = MicrophoneAccessRationale,
// Add more rationales as you add more supported permissions.
}; private static readonly Dictionary<string, string[]> s_requiredPermissionsByWebkitResource = new()
{
[PermissionRequest.ResourceVideoCapture] = new[] { Manifest.Permission.Camera },
[PermissionRequest.ResourceAudioCapture] = new[] { Manifest.Permission.ModifyAudioSettings, Manifest.Permission.RecordAudio },
// Add more Webkit resource -> Android permission mappings as needed.
}; private readonly WebChromeClient _blazorWebChromeClient;
private readonly ComponentActivity _activity;
private readonly ActivityResultLauncher _requestPermissionLauncher; private Action<bool>? _pendingPermissionRequestCallback; public PermissionManagingBlazorWebChromeClient(WebChromeClient blazorWebChromeClient, ComponentActivity activity)
{
_blazorWebChromeClient = blazorWebChromeClient;
_activity = activity;
_requestPermissionLauncher = _activity.RegisterForActivityResult(new ActivityResultContracts.RequestPermission(), this);
} public override void OnCloseWindow(Android.Webkit.WebView? window)
{
_blazorWebChromeClient.OnCloseWindow(window);
_requestPermissionLauncher.Unregister();
} public override void OnGeolocationPermissionsShowPrompt(string? origin, GeolocationPermissions.ICallback? callback)
{
ArgumentNullException.ThrowIfNull(callback, nameof(callback)); RequestPermission(Manifest.Permission.AccessFineLocation, isGranted => callback.Invoke(origin, isGranted, false));
} public override void OnPermissionRequest(PermissionRequest? request)
{
ArgumentNullException.ThrowIfNull(request, nameof(request)); if (request.GetResources() is not { } requestedResources)
{
request.Deny();
return;
} RequestAllResources(requestedResources, grantedResources =>
{
if (grantedResources.Count == 0)
{
request.Deny();
}
else
{
request.Grant(grantedResources.ToArray());
}
});
} private void RequestAllResources(Memory<string> requestedResources, Action<List<string>> callback)
{
if (requestedResources.Length == 0)
{
// No resources to request - invoke the callback with an empty list.
callback(new());
return;
} var currentResource = requestedResources.Span[0];
var requiredPermissions = s_requiredPermissionsByWebkitResource.GetValueOrDefault(currentResource, Array.Empty<string>()); RequestAllPermissions(requiredPermissions, isGranted =>
{
// Recurse with the remaining resources. If the first resource was granted, use a modified callback
// that adds the first resource to the granted resources list.
RequestAllResources(requestedResources[1..], !isGranted ? callback : grantedResources =>
{
grantedResources.Add(currentResource);
callback(grantedResources);
});
});
} private void RequestAllPermissions(Memory<string> requiredPermissions, Action<bool> callback)
{
if (requiredPermissions.Length == 0)
{
// No permissions left to request - success!
callback(true);
return;
} RequestPermission(requiredPermissions.Span[0], isGranted =>
{
if (isGranted)
{
// Recurse with the remaining permissions.
RequestAllPermissions(requiredPermissions[1..], callback);
}
else
{
// The first required permission was not granted. Fail now and don't attempt to grant
// the remaining permissions.
callback(false);
}
});
} private void RequestPermission(string permission, Action<bool> callback)
{
// This method implements the workflow described here:
// https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions if (ContextCompat.CheckSelfPermission(_activity, permission) == Permission.Granted)
{
callback.Invoke(true);
}
else if (_activity.ShouldShowRequestPermissionRationale(permission) && s_rationalesByPermission.TryGetValue(permission, out var rationale))
{
new AlertDialog.Builder(_activity)
.SetTitle("Enable app permissions")!
.SetMessage(rationale)!
.SetNegativeButton("No thanks", (_, _) => callback(false))!
.SetPositiveButton("Continue", (_, _) => LaunchPermissionRequestActivity(permission, callback))!
.Show();
}
else
{
LaunchPermissionRequestActivity(permission, callback);
}
} private void LaunchPermissionRequestActivity(string permission, Action<bool> callback)
{
if (_pendingPermissionRequestCallback is not null)
{
throw new InvalidOperationException("Cannot perform multiple permission requests simultaneously.");
} _pendingPermissionRequestCallback = callback;
_requestPermissionLauncher.Launch(permission);
} void IActivityResultCallback.OnActivityResult(Java.Lang.Object isGranted)
{
var callback = _pendingPermissionRequestCallback;
_pendingPermissionRequestCallback = null;
callback?.Invoke((bool)isGranted);
} #region Unremarkable overrides
// See: https://github.com/dotnet/maui/issues/6565
public override JniPeerMembers JniPeerMembers => _blazorWebChromeClient.JniPeerMembers;
public override Bitmap? DefaultVideoPoster => _blazorWebChromeClient.DefaultVideoPoster;
public override Android.Views.View? VideoLoadingProgressView => _blazorWebChromeClient.VideoLoadingProgressView;
public override void GetVisitedHistory(IValueCallback? callback)
=> _blazorWebChromeClient.GetVisitedHistory(callback);
public override bool OnConsoleMessage(ConsoleMessage? consoleMessage)
=> _blazorWebChromeClient.OnConsoleMessage(consoleMessage);
public override bool OnCreateWindow(WebView? view, bool isDialog, bool isUserGesture, Message? resultMsg)
=> _blazorWebChromeClient.OnCreateWindow(view, isDialog, isUserGesture, resultMsg);
public override void OnGeolocationPermissionsHidePrompt()
=> _blazorWebChromeClient.OnGeolocationPermissionsHidePrompt();
public override void OnHideCustomView()
=> _blazorWebChromeClient.OnHideCustomView();
public override bool OnJsAlert(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsAlert(view, url, message, result);
public override bool OnJsBeforeUnload(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsBeforeUnload(view, url, message, result);
public override bool OnJsConfirm(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsConfirm(view, url, message, result);
public override bool OnJsPrompt(WebView? view, string? url, string? message, string? defaultValue, JsPromptResult? result)
=> _blazorWebChromeClient.OnJsPrompt(view, url, message, defaultValue, result);
public override void OnPermissionRequestCanceled(PermissionRequest? request)
=> _blazorWebChromeClient.OnPermissionRequestCanceled(request);
public override void OnProgressChanged(WebView? view, int newProgress)
=> _blazorWebChromeClient.OnProgressChanged(view, newProgress);
public override void OnReceivedIcon(WebView? view, Bitmap? icon)
=> _blazorWebChromeClient.OnReceivedIcon(view, icon);
public override void OnReceivedTitle(WebView? view, string? title)
=> _blazorWebChromeClient.OnReceivedTitle(view, title);
public override void OnReceivedTouchIconUrl(WebView? view, string? url, bool precomposed)
=> _blazorWebChromeClient.OnReceivedTouchIconUrl(view, url, precomposed);
public override void OnRequestFocus(WebView? view)
=> _blazorWebChromeClient.OnRequestFocus(view);
public override void OnShowCustomView(View? view, ICustomViewCallback? callback)
=> _blazorWebChromeClient.OnShowCustomView(view, callback);
public override bool OnShowFileChooser(WebView? webView, IValueCallback? filePathCallback, FileChooserParams? fileChooserParams)
=> _blazorWebChromeClient.OnShowFileChooser(webView, filePathCallback, fileChooserParams);
#endregion
}

3. 文件 MainPage.xaml

添加 x:Name="_blazorWebView"

    <BlazorWebView x:Name="_blazorWebView" HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type shared:App}" />
</BlazorWebView.RootComponents>
</BlazorWebView>

4. 文件 MainPage.xaml.cs

添加

_blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; _blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing;

完整代码:

using LibraryShared;
using Microsoft.AspNetCore.Components.WebView;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using System;
using static Microsoft.Maui.ApplicationModel.Permissions;
#if ANDROID
using AndroidX.Activity;
#endif namespace BlazorMaui
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent(); _blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized;
_blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing;
} private void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e)
{
#if ANDROID
if (e.WebView.Context?.GetActivity() is not ComponentActivity activity)
{
throw new InvalidOperationException($"The permission-managing WebChromeClient requires that the current activity be a '{nameof(ComponentActivity)}'.");
} e.WebView.Settings.JavaScriptEnabled = true;
e.WebView.Settings.AllowFileAccess = true;
e.WebView.Settings.MediaPlaybackRequiresUserGesture = false;
e.WebView.Settings.SetGeolocationEnabled(true);
e.WebView.Settings.SetGeolocationDatabasePath(e.WebView.Context?.FilesDir?.Path);
e.WebView.SetWebChromeClient(new PermissionManagingBlazorWebChromeClient(e.WebView.WebChromeClient!, activity));
#endif
} private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e)
{
#if IOS || MACCATALYST
e.Configuration.AllowsInlineMediaPlayback = true;
e.Configuration.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None;
#endif
}
}
}

4. 其他更改

由于工程是一个共享库给多端用,先定义了一个接口用于注入服务到页面调用演示功能

    public interface ITools
{
Task<string> CheckPermissionsCamera();
Task<string> TakePhoto(); Task<string> CheckPermissionsLocation();
Task<string> GetCachedLocation(); Task<string> GetCurrentLocation(); Task<string> CheckMock(); double DistanceBetweenTwoLocations(); void ShowSettingsUI();
string GetAppInfo();
}

调用MAUI的API功能 BlazorMaui/Services/TestService.cs

#if WINDOWS
using Windows.Storage;
#endif
#if ANDROID
using Android.Webkit;
#endif
using BlazorShared.Services;
using System.Security.Permissions; namespace LibraryShared
{
public class TestService : ITools
{
public string GetAppInfo() {
//读取应用信息
string name = AppInfo.Current.Name;
string package = AppInfo.Current.PackageName;
string version = AppInfo.Current.VersionString;
string build = AppInfo.Current.BuildString;
return $"{name},{version}.{build}";
} public void ShowSettingsUI()
{
//显示应用设置
AppInfo.Current.ShowSettingsUI();
} public async Task<string> CheckPermissionsCamera()
{
//检查权限的当前状态
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>(); //请求权限
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
} return status.ToString();
}
public async Task<string> CheckPermissionsLocation()
{
//检查权限的当前状态
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>(); //请求权限
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
} return status.ToString();
}
/// <summary>
/// 拍照
/// CapturePhotoAsync调用该方法以打开相机,让用户拍照。 如果用户拍照,该方法的返回值将是非 null 值。
/// 以下代码示例使用媒体选取器拍摄照片并将其保存到缓存目录:
/// </summary>
public async Task<string> TakePhoto()
{
await CheckPermissionsCamera(); if (MediaPicker.Default.IsCaptureSupported)
{
FileResult photo = await MediaPicker.Default.CapturePhotoAsync(); if (photo != null)
{
// save the file into local storage
string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName); using Stream sourceStream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath); await sourceStream.CopyToAsync(localFileStream);
return localFilePath;
}
return "photo null"; } return null;
} /// <summary>
/// 获取最后一个已知位置, 设备可能已缓存设备的最新位置。
/// 使用此方法 GetLastKnownLocationAsync 访问缓存的位置(如果可用)。
/// 这通常比执行完整位置查询更快,但可能不太准确。
/// 如果不存在缓存位置,此方法将 null返回 。
/// </summary>
/// <returns></returns>
public async Task<string> GetCachedLocation()
{
await CheckPermissionsLocation();
string result = null;
try
{
Location location = await Geolocation.Default.GetLastKnownLocationAsync(); if (location != null)
{
result = $"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}";
Console.WriteLine(result);
return result;
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
result = $"not supported on device, {fnsEx.Message}";
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
result = $"not enabled on device, {fneEx.Message}";
}
catch (PermissionException pEx)
{
// Handle permission exception
result = $"permission, {pEx.Message}";
}
catch (Exception ex)
{
// Unable to get location
result = $"Unable to get location, {ex.Message}";
} return result ?? "None";
} private CancellationTokenSource _cancelTokenSource;
private bool _isCheckingLocation; /// <summary>
/// 获取当前位置
/// 虽然检查设备 的最后已知位置 可能更快,但它可能不准确。
/// 使用该方法 GetLocationAsync 查询设备的当前位置。
/// 可以配置查询的准确性和超时。
/// 最好是使用 GeolocationRequest 和 CancellationToken 参数的方法重载,
/// 因为可能需要一些时间才能获取设备的位置。
/// </summary>
/// <returns></returns>
public async Task<string> GetCurrentLocation()
{
await CheckPermissionsLocation();
string result = null;
try
{
_isCheckingLocation = true; GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10)); _cancelTokenSource = new CancellationTokenSource(); #if IOS
//从 iOS 14 开始,用户可能会限制应用检测完全准确的位置。
//该 Location.ReducedAccuracy 属性指示位置是否使用降低的准确性。
//若要请求完全准确性,请将 GeolocationRequest.RequestFullAccuracy 属性设置为 true
request.RequestFullAccuracy = true;
#endif Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token); if (location != null)
{
result = $"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}";
Console.WriteLine(result);
return result;
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
result = $"not supported on device, {fnsEx.Message}";
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
result = $"not enabled on device, {fneEx.Message}";
}
catch (PermissionException pEx)
{
// Handle permission exception
result = $"permission, {pEx.Message}";
}
catch (Exception ex)
{
// Unable to get location
result = $"Unable to get location, {ex.Message}";
}
finally
{
_isCheckingLocation = false;
}
return result ?? "None";
}
}
}

MauiProgram.cs文件注入

builder.Services.AddSingleton<ITools, TestService>();

razor

        <Button Text="定位权限" OnClick="检查定位权限" />
<span>@定位权限</span><br/><br/>
<Button Text="摄像机权限" OnClick="检查摄像机权限" />
<span>@摄像机权限</span><br /><br />
<Button Text="定位" OnClick="获取定位" />
<span>@Locations</span><br /><br />
<Button Text="TakePhoto" OnClick="TakePhoto" />
<span>@PhotoFilename</span><br /><br />
<Button Text="ShowSettings" OnClick="ShowSettingsUI" />
<span>@version</span><br /><br /> @code{
[Inject] protected ITools Tools { get; set; } private string Locations;
private string PhotoFilename;
private string version;
private string 定位权限;
private string 摄像机权限; async Task 获取定位() => Locations = await Tools.GetCurrentLocation();
async Task TakePhoto() => PhotoFilename = await Tools.TakePhoto();
async Task 检查定位权限() => 定位权限 = await Tools.CheckPermissionsLocation();
async Task 检查摄像机权限() => 摄像机权限 = await Tools.CheckPermissionsCamera();
void ShowSettingsUI() => Tools.ShowSettingsUI();
}

最终效果


项目地址

https://github.com/densen2014/BlazorMaui

https://gitee.com/densen2014/BlazorMaui

参考资料

Permissions

https://docs.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/permissions?tabs=android

Geolocation

https://docs.microsoft.com/en-us/dotnet/maui/platform-integration/device/geolocation?tabs=windows

MauiBlazorPermissionsExample

https://github.com/MackinnonBuck/MauiBlazorPermissionsExample

关联项目

FreeSql QQ群:4336577、8578575、52508226

BA & Blazor QQ群:795206915、675147445

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

AlexChow

今日头条 | 博客园 | 知乎 | Gitee | GitHub

MAUI Blazor 权限经验分享 (定位,使用相机)的更多相关文章

  1. 【原创经验分享】WCF之消息队列

    最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...

  2. (转)CMOS Sensor的调试经验分享

    CMOS Sensor的调试经验分享 我这里要介绍的就是CMOS摄像头的一些调试经验. 首先,要认识CMOS摄像头的结构.我们通常拿到的是集成封装好的模组,一般由三个部分组成:镜头.感应器和图像信号处 ...

  3. thinkphp开发技巧经验分享

    thinkphp开发技巧经验分享 www.111cn.net 编辑:flyfox 来源:转载 这里我给大家总结一个朋友学习thinkphp时的一些笔记了,从变量到内置模板引擎及系统变量等等的笔记了,同 ...

  4. 线上Linux服务器运维安全策略经验分享

    线上Linux服务器运维安全策略经验分享 https://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=402022683&idx=1&a ...

  5. CMOS Sensor的调试经验分享

    转自:http://bbs.52rd.com/forum.php?mod=viewthread&tid=276351 CMOS Sensor的调试经验分享 我这里要介绍的就是CMOS摄像头的一 ...

  6. 关于Java解压文件的一些坑及经验分享(MALFORMED异常)

    文章也已经同步到我的csdn博客: http://blog.csdn.net/u012881584/article/details/72615481 关于Java解压文件的一些坑及经验分享 就在本周, ...

  7. 沉淀,再出发——在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享

    在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享 一.工作准备 首先,明确工作的重心,在Ubuntu Kylin15.04中配置Hadoop集群,这里我是用的双系统中的 ...

  8. CIO必看:跨国集团采购部报表系统的建设经验分享

    CIO必看:跨国集团采购部报表系统的建设经验分享 引言 福耀集团是国内最具规模.技术水平最高.出口量最大的汽车玻璃生产供应商,产品"FY"商标是中国汽车玻璃行业第一个"中 ...

  9. CMOS Sensor的调试经验分享【转】

    转自:https://blog.csdn.net/yapingmcu/article/details/37817727 转自:http://bbs.52rd.com/forum.php?mod=vie ...

随机推荐

  1. MySQL启动与多实例安装

    启动方式及故障排查 一.几个问题 1.1 /etc/init.d/mysql 从哪来 cp /usr/local/mysql/support-files/mysql.server /etc/init. ...

  2. Mock 之搭建本地 MockJs

    Mock 之搭建本地 MockJs 一.目的 模拟后端接口 二.发请求 1. install npm install axios 2. 配置 src/utils/request.js import a ...

  3. Python实现将excel文件转化为html文件

    需要转化的excel文件(nsrxx.xlsx): 源代码: import pandas as pdimport codecspd.set_option('display.width', 1000)p ...

  4. 【Azure 存储服务】Java Azure Storage SDK V12使用Endpoint连接Blob Service遇见 The Azure Storage endpoint url is malformed

    问题描述 使用Azure Storage Account的共享访问签名(Share Access Signature) 生成的终结点,连接时遇见  The Azure Storage endpoint ...

  5. 参与 2022 第二季度 Flutter 开发者调查

    2022 Google I/O 大会正式落下帷幕,Flutter 作为 14 个开发者产品和平台中的一款,吸引了来自全球的很多开发者们的关注.随着全国很多地方已经进入夏季,Flutter 今年第二季度 ...

  6. Django+Vue+Nginx+Https域名代理访问

    Django+Vue使用Nginx实现Https域名的安全访问 前端 VUE 前端访问自身域名: https://demo.com,后序使用 Nginx 代理至后端 直接访问后端https:api会无 ...

  7. 『忘了再学』Shell基础 — 29、AWK内置变量

    目录 1.AWK内置变量 2.练习说明 (1)$n变量练习 (2)FS变量练习 (3)NF变量和NR变量练习 3.总结: 1.AWK内置变量 AWK内置变量如下表: awk内置变量 作用 $0 代表目 ...

  8. VisonPro · 视觉工具列表说明

  9. 【python基础】第02回 计算机基础2

    上节内容回顾 1.绝对路径与相对路径 1.路径的概念 用来标识资源的位置 2.绝对路径 类似于全球GPS定位(给到任何人都可以顺利的找到相应的资源) eg: D:\aaa\a.txt 3.相对路径 需 ...

  10. idea部署项目运行没问题,但是页面404。

    解决方案: 这个位置不要添加内容. 参考:https://blog.csdn.net/hupixiong/article/details/105443606