精华内容
下载资源
问答
  • 使用多线程下载资源后,分片上传至阿里oss(主流对象存储都支持分片上传)。实现边下载边上传 注意:下载资源url必须支持部分资源返回。http状态码:206 下载执行类DownloadTask 该类实现callable接口,负责下载...
    使用多线程下载资源后,分片上传至阿里oss(主流对象存储都支持分片上传)。实现边下载边上传
    

    注意:下载的资源url必须支持部分资源返回。http状态码:206

    下载执行类DownloadTask

    该类实现callable接口,负责下载资源的某一部分

    使用CloseableHttpClient进行下载

    package com.tinet.clink.chat.web.service.rtc;
    
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.util.EntityUtils;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.concurrent.Callable;
    
    
    /**
     * @author dengsx
     * @create 2021-06-11
     **/
    @Slf4j
    class DownloadTask implements Callable<InputStream> {
        private final String url;
        private final long lowerBound; // 下载的文件区间
        private final long upperBound;
        private final CloseableHttpClient client;
    
        DownloadTask(String url, long lowerBound, long upperBound, CloseableHttpClient client) {
            this.url = url;
            this.lowerBound = lowerBound;
            this.upperBound = upperBound;
            this.client = client;
        }
    
        @Override
        public InputStream call() throws Exception {
            try (InputStream input = connect()) {
                log.info("线程{}下载完成,下载区间:{}-{},下载字节:{}", Thread.currentThread().getName(),
                        this.lowerBound, this.upperBound, input.available());
                return input;
            } catch (IOException e) {
                throw new IOException(e.getMessage());
            }
        }
    
        /**
         * 连接WEB服务器,并返回一个数据流
         *
         * @return 返回输入流
         * @throws IOException 网络连接错误
         */
        private InputStream connect() throws IOException {
            HttpGet get = new HttpGet(url);
            get.setHeader("Range", "bytes=" + lowerBound + "-" + upperBound);
            try (CloseableHttpResponse response = client.execute(get)) {
                log.info("线程{}连接成功开始下载", Thread.currentThread().getName());
                int statusCode = response.getStatusLine().getStatusCode();
                if (HttpStatus.SC_PARTIAL_CONTENT != statusCode) {
                    throw new IOException("下载连接不支持多线程下载:" + statusCode);
                }
    
                return new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
            } catch (IOException e) {
                throw new IOException("下出错:" + e.getMessage());
            }
        }
    }
    

    上传任务类UploadTask

    该类负责将下载的资源上传至oss,使用阿里oss的分片上传

    package com.tinet.clink.chat.web.service.rtc;
    
    import com.tinet.clink.common.model.PartETagAdapter;
    import com.tinet.clink.common.objectstorage.ObjectStorageClient;
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.concurrent.Callable;
    
    
    /**
     * @author dengsx
     * @create 2021-06-11
     **/
    @Slf4j
    public class UploadTask implements Callable<PartETagAdapter> {
        private final InputStream inputStream;
        private final ObjectStorageClient objectStorageClient;
        private final int order;
        private final long partSize;
        private final String uploadId;
        private final String fileBucket;
        private final String key;
    
        public UploadTask(InputStream inputStream, ObjectStorageClient objectStorageService, String fileBucket, String key, String uploadId, int order, long partSize) {
            this.inputStream = inputStream;
            this.objectStorageClient = objectStorageService;
            this.order = order;
            this.uploadId = uploadId;
            this.key = key;
            this.fileBucket = fileBucket;
            this.partSize = partSize;
        }
    
        @Override
        public PartETagAdapter call() throws IOException {
            log.info("开始上传分片,分片order:{},分片大小:{}", order, partSize);
            return objectStorageClient.uploadPartObject(fileBucket, key, uploadId, inputStream, order, partSize);
        }
    }
    
    

    下载器类Downloader

    该类负责,分配任务给线程下载,上传,合并结果

    package com.tinet.clink.chat.web.service.rtc;
    
    import com.tinet.clink.common.model.CompleteMultipartUploadResultAdapter;
    import com.tinet.clink.common.model.PartETagAdapter;
    import com.tinet.clink.common.objectstorage.ObjectStorageClient;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HeaderElement;
    import org.apache.http.HeaderElementIterator;
    import org.apache.http.client.methods.HttpHead;
    import org.apache.http.conn.ConnectionKeepAliveStrategy;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.message.BasicHeaderElementIterator;
    import org.apache.http.protocol.HTTP;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.*;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 从腾讯云下载视频上传至oss
     * 多线程下载
     * 边下载边上传
     *
     * @author dengsx
     * @create 2021-06-11
     **/
    @Component
    @Slf4j
    public class Downloader implements InitializingBean {
    
    
        private CloseableHttpClient httpClient;
    
        private static final int DEFAULT_THREAD_COUNT;
    
        /**
         * 阻塞系数 大致 0.5
         */
        private static final double BLOCK_FACTOR = 0.5;
    
        static {
            // io密集型任务,线程数设置为cpu核数的两倍
            Double threadCount = Runtime.getRuntime().availableProcessors() / (1 - BLOCK_FACTOR);
            DEFAULT_THREAD_COUNT = threadCount.intValue();
        }
    
        @Autowired
        @Qualifier("downloaderTaskExecutor")
        private ThreadPoolTaskExecutor downloaderTaskExecutor;
    
        /**
         * 会话文件上传的桶
         */
        @Value("${object.storage.bucket.chat}")
        private String fileBucket;
    
        @Autowired
        private ObjectStorageClient objectStorageClient;
    
        public CompleteMultipartUploadResultAdapter start(String url, String fileKey) throws Exception {
            return this.start(url, DEFAULT_THREAD_COUNT, fileKey);
        }
    
        public CompleteMultipartUploadResultAdapter start(String url, Integer threadCount, String fileKey) throws Exception {
    
            if (threadCount == null || threadCount < 0) {
                throw new IllegalArgumentException("invalid threadCount number!");
            }
    
            threadCount = Math.min(DEFAULT_THREAD_COUNT, threadCount);
    
            long fileSize = getFileSize(url);
    
            log.info("开始下载,文件大小(kb):{},fileSize(Mb):{},下载线程数:{}", fileSize, fileSize / 1024.0 / 1024, threadCount);
    
            //记录时间
            long beginTime = System.currentTimeMillis();
            // 分配线程下载
            Map<Integer, Future<InputStream>> downloadFuture = dispatcher(url, fileSize, threadCount);
    
            //分片上传id
            String uploadId = objectStorageClient.getMultipartUploadId(fileBucket, fileKey);
    
            // 处理下载结果
            List<Future<PartETagAdapter>> uploadFuture = handleDownloadFuture(downloadFuture, uploadId, fileKey, threadCount, fileSize);
    
            //处理上传结果
            List<PartETagAdapter> uploadResult = handleUploadFuture(uploadFuture);
    
            //分片上传合并
            CompleteMultipartUploadResultAdapter result = objectStorageClient.completePartlyUpload(fileBucket, fileKey, uploadId, uploadResult);
    
            log.info("下载成功并上传oss成功:本次用时:{}秒,上传结果{},", (System.currentTimeMillis() - beginTime) / 1000, result.getKey());
            return result;
    
        }
    
        /**
         * 分配器,决定每个线程下载哪个区间的数据
         */
        private Map<Integer, Future<InputStream>> dispatcher(String url, long fileSize, int threadCount) {
            long blockSize = fileSize / threadCount; // 每个线程要下载的数据量
            long lowerBound;
            long upperBound;
            Map<Integer, Future<InputStream>> result = new HashMap<>();
            for (int i = 0; i < threadCount; i++) {
    
                lowerBound = i * blockSize;
                // 最后一个分片的上限是文件大小
                upperBound = (i == threadCount - 1) ? fileSize - 1 : lowerBound + blockSize;
                // 异步提交下载任务
                Future<InputStream> future = downloaderTaskExecutor.submit(new DownloadTask(url, lowerBound, upperBound, httpClient));
                result.put(i + 1, future);
            }
            return result;
        }
    
    
        /**
         * @return 要下载的文件的尺寸
         */
        private long getFileSize(String url) throws IOException {
            HttpHead head = new HttpHead(url);
            String lengthValue = httpClient.execute(head).getHeaders("Content-Length")[0].getValue();
            return Long.parseLong(lengthValue);
    
        }
    
        private List<Future<PartETagAdapter>> handleDownloadFuture(Map<Integer, Future<InputStream>> downloadFuture,
                                                                   String uploadId, String key,
                                                                   int threadCount, long fileSize) throws Exception {
            long blockSize = fileSize / threadCount;
            List<Future<PartETagAdapter>> uploadFuture = new ArrayList<>();
            while (!downloadFuture.isEmpty()) {
                for (Iterator<Map.Entry<Integer, Future<InputStream>>> it = downloadFuture.entrySet().iterator(); it.hasNext(); ) {
                    Map.Entry<Integer, Future<InputStream>> currentFuture = it.next();
                    if (currentFuture.getValue().isDone()) {
                        Integer partNumber = currentFuture.getKey();
                        long partSize = blockSize;
                        if (Objects.equals(partNumber, threadCount)) {
                            partSize = fileSize - (threadCount - 1) * blockSize;
                        }
                        InputStream inputStream = currentFuture.getValue().get();
    
                        //异步提交上传任务
                        Future<PartETagAdapter> tagFuture = downloaderTaskExecutor.submit(new UploadTask(inputStream,
                                objectStorageClient, fileBucket, key, uploadId, currentFuture.getKey(), partSize));
                        uploadFuture.add(tagFuture);
                        it.remove();
                    }
                }
                sleep(1000);
            }
            log.info("下载结果处理完成,上传任务提交完成");
            return uploadFuture;
        }
    
        private List<PartETagAdapter> handleUploadFuture(List<Future<PartETagAdapter>> partETagsFutures) throws Exception {
            List<PartETagAdapter> uploadResult = new ArrayList<>();
            while (!partETagsFutures.isEmpty()) {
                for (Iterator<Future<PartETagAdapter>> iterator = partETagsFutures.iterator(); iterator.hasNext(); ) {
                    Future<PartETagAdapter> next = iterator.next();
                    if (next.isDone()) {
                        PartETagAdapter partETag = next.get();
                        uploadResult.add(partETag);
                        iterator.remove();
                    }
                }
                sleep(1000);
            }
            log.info("上传任务结果处理完成");
            return uploadResult;
        }
    
        private void sleep(int millis) {
            try {
                TimeUnit.MILLISECONDS.sleep(millis);
            } catch (InterruptedException e) {
                //ignore
            }
        }
    
        @Override
        public void afterPropertiesSet() {
            // 设置keep alive的连接策略
            ConnectionKeepAliveStrategy strategy = (response, context) -> {
                // Honor 'keep-alive' header
                HeaderElementIterator it = new BasicHeaderElementIterator(
                        response.headerIterator(HTTP.CONN_KEEP_ALIVE));
                while (it.hasNext()) {
                    HeaderElement he = it.nextElement();
                    String param = he.getName();
                    String value = he.getValue();
                    if (value != null && "timeout".equalsIgnoreCase(param)) {
                        return Long.parseLong(value) * 1000;
                    }
                }
                return 5 * 1000;
            };
    
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
            connectionManager.setMaxTotal(20);
            connectionManager.setDefaultMaxPerRoute(5);
    
            httpClient = HttpClients.custom()
                    .setKeepAliveStrategy(strategy).setConnectionManager(connectionManager).build();
        }
    }
    
    
    

    阿里oss分片上传步骤

    1.获取分片上传id
        /**
         * 获取分片上传id
         *
         * @param bucketName
         * @param key
         * @return
         */
        public String getMultipartUploadId(String bucketName, String key) {
            // 创建InitiateMultipartUploadRequest对象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
    
            // 如果需要在初始化分片时设置文件存储类型,请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // request.setObjectMetadata(metadata);
    
            // 初始化分片。
            InitiateMultipartUploadResult upResult = getInternalOSSClient().initiateMultipartUpload(request);
            // 返回uploadId,它是分片上传事件的唯一标识,可以根据这个uploadId发起相关的操作,如取消分片上传、查询分片上传等。
            return upResult.getUploadId();
        }
    
    2.上传分片

    只需要将分片的流等参数准备好即可

        /**
         * 分片上传
         *
         * @param bucketName
         * @param key
         * @param uploadId
         * @param inputStream 要上传的流
         * @param order       分片上传的序号,后面根据这序号拼接完整的文件
         * @return
         */
        public PartETagAdapter uploadPartObject(String bucketName, String key, String uploadId, InputStream inputStream, int order, long fileSize) throws IOException {
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(bucketName);
            uploadPartRequest.setKey(key);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setInputStream(inputStream);
            // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。
            uploadPartRequest.setPartSize(fileSize);
            // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出这个范围,OSS将返回InvalidArgument的错误码。
            uploadPartRequest.setPartNumber(order);
            // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
            PartETagAdapter ossPartETagAdapter = new PartETagAdapter();
            ossPartETagAdapter.setOssPartETag(getInternalOSSClient().uploadPart(uploadPartRequest).getPartETag());
            return ossPartETagAdapter;
        }
    
    3.合并上传结果
        /**
         * 完成分片上传
         *
         * @param bucketName
         * @param key
         * @param uploadId
         * @param partETags
         * @return
         */
        public CompleteMultipartUploadResultAdapter completePartlyUpload(String bucketName, String key, String uploadId, List<PartETagAdapter> partETags) {
            // 创建CompleteMultipartUploadRequest对象。
            // 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(bucketName, key, uploadId,
                            partETags.stream().map(PartETagAdapter::getOssPartETag).collect(Collectors.toList()));
    
            // 如果需要在完成文件上传的同时设置文件访问权限,请参考以下示例代码。
            // completeMultipartUploadRequest.setObjectACL(CannedAccessControlList.PublicRead);
    
            // 完成上传。
            CompleteMultipartUploadResultAdapter resultAdapter = new CompleteMultipartUploadResultAdapter();
            resultAdapter.setOssMultiPartUploadResult(getInternalOSSClient().completeMultipartUpload(completeMultipartUploadRequest));
            return resultAdapter;
        }
    

    end~

    展开全文
  • C/C++开发资源下载汇总

    千次阅读 2021-11-19 10:07:00
    开个新帖子,记录需要用到的网络资源 runtime官网 Latest supported Visual C++ Redistributable downloads | Microsoft DocsThis article lists the download links for the latest versions of Visual C++ ...

    开个新帖子,记录需要用到的网络资源

    runtime官网

    Latest supported Visual C++ Redistributable downloads | Microsoft DocsThis article lists the download links for the latest versions of Visual C++ Redistributable packages.https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-170


    Using GCC with MinGW

    In this tutorial, you configure Visual Studio Code to use the GCC C++ compiler (g++) and GDB debugger from mingw-w64 to create programs that run on Windows.

    MinGW-w64GCC for Windows 64 & 32 bitshttps://www.mingw-w64.org/我选的是MSYS2

    MSYS2

    安装完成,运行MSYS2 64bit

     First run pacman -Syu         进行升级

    Run "MSYS2 MSYS" from Start menu. Update the rest of the base packages with pacman -Su:

    Now MSYS2 is ready for you. You will probably want to install some tools and the mingw-w64 GCC to start compiling:

    pacman -S --needed base-devel mingw-w64-x86_64-toolchain

    配置VSCODE

    Prerequisites#

    To successfully complete this tutorial, you must do the following steps:

    Check your MinGW installation#

    1. Install Visual Studio Code.

    2. Install the C/C++ extension for VS Code. You can install the C/C++ extension by searching for 'c++' in the Extensions view (Ctrl+Shift+X).

    1. Get the latest version of Mingw-w64 via MSYS2, which provides up-to-date native builds of GCC, Mingw-w64, and other helpful C++ tools and libraries. Click here to download the MSYS2 installer. Then follow the instructions on the MSYS2 website to install Mingw-w64.

    2. Add the path to your Mingw-w64 bin folder to the Windows PATH environment variable by using the following steps:

      1. In the Windows search bar, type 'settings' to open your Windows Settings.
      2. Search for Edit environment variables for your account.
      3. Choose the Path variable and then select Edit.
      4. Select New and add the Mingw-w64 destination folder path to the system path. The exact path depends on which version of Mingw-w64 you have installed and where you installed it. If you used the settings above to install Mingw-w64, then add this to the path: C:\msys64\mingw64\bin.
      5. Select OK to save the updated PATH. You will need to reopen any console windows for the new PATH location to be available.

    To check that your Mingw-w64 tools are correctly installed and available, open a new Command Prompt and type:

    g++ --version
    gdb --version

    If you don't see the expected output or g++ or gdb is not a recognized command, make sure your PATH entry matches the Mingw-w64 binary location where the compilers are located. If the compilers do not exist at that PATH entry, make sure you followed the instructions on the MSYS2 website to install Mingw-w64.

    Create Hello World#

    From a Windows command prompt, create an empty folder called projects where you can place all your VS Code projects. Then create a sub-folder called helloworld, navigate into it, and open VS Code in that folder by entering the following commands:

    mkdir projects
    cd projects
    mkdir helloworld
    cd helloworld
    code .

    The "code ." command opens VS Code in the current working folder, which becomes your "workspace". As you go through the tutorial, you will see three files created in a .vscode folder in the workspace:

    #include <iostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};
    
        for (const string& word : msg)
        {
            cout << word << " ";
        }
        cout << endl;
    }
    • tasks.json (build instructions)
    • launch.json (debugger settings)
    • c_cpp_properties.json (compiler path and IntelliSense settings)

    Add a source code file#

    In the File Explorer title bar, select the New File button and name the file helloworld.cpp.

    Build helloworld.cpp#

    Next, you'll create a tasks.json file to tell VS Code how to build (compile) the program. This task will invoke the g++ compiler to create an executable file based on the source code.

    From the main menu, choose Terminal > Configure Default Build Task. In the dropdown, which will display a tasks dropdown listing various predefined build tasks for C++ compilers. Choose g++.exe build active file, which will build the file that is currently displayed (active) in the editor.

    This will create a tasks.json file in a .vscode folder and open it in the editor.

    Your new tasks.json file should look similar to the JSON below:

    {
      "tasks": [
        {
          "type": "cppbuild",
          "label": "C/C++: g++.exe build active file",
          "command": "C:/msys64/mingw64/bin/g++.exe",
          "args": ["-g", "${file}", "-o", "${fileDirname}\\${fileBasenameNoExtension}.exe"],
          "options": {
            "cwd": "${fileDirname}"
          },
          "problemMatcher": ["$gcc"],
          "group": {
            "kind": "build",
            "isDefault": true
          },
          "detail": "compiler: C:/msys64/mingw64/bin/g++.exe"
        }
      ],
      "version": "2.0.0"
    }

    The command setting specifies the program to run; in this case that is g++. The args array specifies the command-line arguments that will be passed to g++. These arguments must be specified in the order expected by the compiler. This task tells g++ to take the active file (${file}), compile it, and create an executable file in the current directory (${fileDirname}) with the same name as the active file but with the .exe extension (${fileBasenameNoExtension}.exe), resulting in helloworld.exe for our example.

    Note: You can learn more about tasks.json variables in the variables reference.

    The label value is what you will see in the tasks list; you can name this whatever you like.

    The "isDefault": true value in the group object specifies that this task will be run when you press Ctrl+Shift+B. This property is for convenience only; if you set it to false, you can still run it from the Terminal menu with Tasks: Run Build Task.

    Running the build#

    • Go back to helloworld.cpp. Your task builds the active file and you want to build helloworld.cpp.

    • To run the build task defined in tasks.json, press Ctrl+Shift+B or from the Terminal main menu choose Run Build Task.

    • When the task starts, you should see the Integrated Terminal panel appear below the source code editor. After the task completes, the terminal shows output from the compiler that indicates whether the build succeeded or failed. For a successful g++ build, the output looks something like this:

    • Create a new terminal using the + button and you'll have a new terminal with the helloworld folder as the working directory. Run dir and you should now see the executable helloworld.exe.

    1. You can run helloworld in the terminal by typing helloworld.exe (or .\helloworld.exe if you use a PowerShell terminal).

    Note: You might need to press Enter a couple of times initially to see the PowerShell prompt in the terminal. This issue should be fixed in a future release of Windows.

    Modifying tasks.json#

    You can modify your tasks.json to build multiple C++ files by using an argument like "${workspaceFolder}\\*.cpp" instead of ${file}. This will build all .cpp files in your current folder. You can also modify the output filename by replacing "${fileDirname}\\${fileBasenameNoExtension}.exe" with a hard-coded filename (for example "${workspaceFolder}\\myProgram.exe").

    Debug helloworld.cpp#

    Next, you'll create a launch.json file to configure VS Code to launch the GDB debugger when you press F5 to debug the program.

    1. From the main menu, choose Run > Add Configuration... and then choose C++ (GDB/LLDB).
    2. You'll then see a dropdown for various predefined debugging configurations. Choose g++.exe build and debug active file.

    VS Code creates a launch.json file, opens it in the editor, and builds and runs 'helloworld'.

    {
      "version": "0.2.0",
      "configurations": [
        {
          "name": "g++.exe - Build and debug active file",
          "type": "cppdbg",
          "request": "launch",
          "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
          "args": [],
          "stopAtEntry": false,
          "cwd": "${fileDirname}",
          "environment": [],
          "externalConsole": false,
          "MIMode": "gdb",
          "miDebuggerPath": "C:\\msys64\\mingw64\\bin\\gdb.exe",
          "setupCommands": [
            {
              "description": "Enable pretty-printing for gdb",
              "text": "-enable-pretty-printing",
              "ignoreFailures": true
            }
          ],
          "preLaunchTask": "C/C++: g++.exe build active file"
        }
      ]
    }

    The program setting specifies the program you want to debug. Here it is set to the active file folder ${fileDirname} and active filename with the .exe extension ${fileBasenameNoExtension}.exe, which if helloworld.cpp is the active file will be helloworld.exe.

    By default, the C++ extension won't add any breakpoints to your source code and the stopAtEntry value is set to false.

    Change the stopAtEntry value to true to cause the debugger to stop on the main method when you start debugging.

    Note: The preLaunchTask setting is used to specify task to be executed before launch. Make sure it is consistent with the tasks.json file label setting.

    Start a debugging session#

    1. Go back to helloworld.cpp so that it is the active file.
    2. Press F5 or from the main menu choose Run > Start Debugging. Before you start stepping through the source code, let's take a moment to notice several changes in the user interface:
    • The Integrated Terminal appears at the bottom of the source code editor. In the Debug Output tab, you see output that indicates the debugger is up and running.

    • The editor highlights the first statement in the main method. This is a breakpoint that the C++ extension automatically sets for you:

    • The Run view on the left shows debugging information. You'll see an example later in the tutorial.

    • At the top of the code editor, a debugging control panel appears. You can move this around the screen by grabbing the dots on the left side.

    Step through the code#

    展开全文
  • Android Webview H5资源本地化一. 创建读取资源项目独立模块1. 项目依赖的好处符合模块化的思想,他们相互独立。一个项目持有另一个项目的引用,修改更加方便。(注:compile project编译引用的项目其内容必须包含有...

    Android Webview H5资源本地化

    一. 创建读取资源项目独立模块

    1. 项目依赖的好处

    符合模块化的思想,他们相互独立。一个项目持有另一个项目的引用,修改更加方便。

    (注:compile project编译引用的项目其内容必须包含有java代码、xml布局文件、AndroidManifest等,而且还要在项目的setting.gradle中用include的形式声明引用)

    2. 操作步骤导入项目ProjectR

    被依赖的项目ProjectR不需要任何改动!

    1. 在需要使用的项目中的settings.gradle添加配置

    include ':ProjectR'

    project(':ProjectR').projectDir = new File(settingsDir,'../../ProjectR/')

    include ':ProjectR:app'

    2. 在需要使用的项目中的Module中添加需要引入的library

    dependencies {

    ...

    compile project(path: ':ProjectR:app')

    ...

    }

    3. 设置正确的被依赖的项目路径

    project(':BProject').projectDir = new File(settingsDir,'../../ProjectR/')

    其中 new File(settingsDir,'../../ProjectR/')

    参数说明:

    参数一: settingsDir 指的是相对于 settings.gradle 文件所在路径

    参数二: 填写被依赖项目的路径,**../**表示上级目录,所以根据自己的路径修改

    3. 坑(注意)

    如果你不小心填错了被依赖项目的路径,而且还点了同步项目。那么可能会在Project和Module 目录下生成类似 xxx_xxx.iml 的文件,如果异常文件存在,后面就算你的路径配置正确也可能同步不成功,不断的提示错误。这是你只需要删除上叙文件同步项目即可。

    4. Assets资源文件读取和AssetManager

    AssetManager管理对assets文件夹资源的访问,它允许你以简单的字节流的形式打开和读取和应用程序绑定在一起的原始资源文件。主要用到list()及open()方法。

    注意:要创建一个assets文件夹和java文件夹同级,然后再asset创建一个文件夹命H5Res,此句不要assets不然会找不到文件异常处理

    InputStream stream = assetManager.open("H5Res/" + resUrl);

    finalString[]        list(Stringpath)     返回指定路径下的所有文件及目录名,path是相对路径,是assets子目录。

    finalInputStream     open(String fileName)      使用 ACCESS_STREAMING模式打开assets下的指定文件,fileName是相对路径,是assets子目录。

    finalInputStream      open(String fileName,int accessMode)   使用显示的访问模式打开assets下的指定文件。

    5. ProjectR项目主要代码

    import android.app.Activity;

    import android.content.res.AssetManager;

    import android.webkit.MimeTypeMap;

    import android.webkit.WebResourceResponse;

    import android.webkit.WebView;

    import java.io.FileNotFoundException;

    import java.io.InputStream;

    public class LocalAssets{

    private static LocalAssets _instance = null;

    public static LocalAssets getInstance() {

    return _instance != null ? _instance : (_instance = new LocalAssets());

    }

    private Activity mainContext = null;

    // 用于保存请求链接截取出的本地路径

    private String resUrl = "";

    /**

    * 初始化此类获得上下文

    */

    public boolean init(Activity context) {

    mainContext = context;

    return true;

    }

    /**

    * 拦截webview请求地址换成读取本地文件

    */

    public WebResourceResponse doLoadRes(WebView view, String url) {

    try {

    // 根据url判断是否是需要加载的本地资源进行拦截替换响应资源

    if (url === 'assets') {

    AssetManager assetManager = mainContext.getAssets();

    InputStream stream = assetManager.open("H5Res/" + resUrl);

    WebResourceResponse response = new WebResourceResponse(

    MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url))

    , "UTF-8", stream

    );

    return response;

    }

    } catch (FileNotFoundException e) {

    } catch (Exception e) {

    e.printStackTrace();

    }

    return null;

    }

    }

    二. 主项目代码

    1. 将需要本地化的资源放入assets/H5Res文件夹下。

    2. 设置完webview后初始化导入的库:

    LocalAssets.getInstance().init(this);

    3. 设置webview拦截请求:

    @Override

    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

    WebResourceResponse resp = LocalAssets.getInstance().doLoadRes(view, url);

    if (resp != null) {

    return resp;

    }

    return super.shouldInterceptRequest(view, url);

    }

    @Override

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)

    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

    WebResourceResponse resp = LocaalAssets.getInstance().doLoadRes(view, request.getUrl().toString());

    if (resp != null) {

    return resp;

    }

    return super.shouldInterceptRequest(view, request);

    }

    Atitit&period;android&&num;160&semi;webview&&num;160&semi;h5运行环境总结

    Atitit.android webview h5运行环境总结 1. WebView 的使用1 2. Js调用java1 3. Js调用java 跟个swt的比较2 3.1. Swt是BrowserF ...

    Android WebView H5开发拾遗

    上篇介绍了一些WebView的设置,本篇为一些补充项. 首先加载HTML5的页面时,会出现页面空白的现象,原因是需要开启 DOM storage API 功能: webSettings.setDomS ...

    H5静态资源本地化实践

    现在很多app都是通过webview内嵌H5的页面,这种方式的好处就是无需发版就能更新线上的内容,而且可以做到多平台的统一开发,节约开发成本.但是这种模式也带来了一定的问题,web开发很大程度依赖于网 ...

    H5 hybrid开发-前端资源本地化方案纪要

    H5 hybrid-前端资源本地化方案纪要 就整个行业来说,大前端是趋势,现阶段,native方面除了一些偏CPU密集型工作与操作系统底层API方面的工作外,H5基本都可以满足需要. 目前的工作更偏向 ...

    Android WebView与H5联调技巧

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/78 背景: 突然想写一篇关于Android WebView ...

    Android WebView 不支持 H5 input type&equals;&quot&semi;file&quot&semi; 解决方法

    最近因为赶项目进度,因此将本来要用原生控件实现的界面,自己做了H5并嵌入webview中.发现点击H5中 标签 不能打开android资源管理器. 通过网络搜索发现是因为 android webvie ...

    Android原生和H5交互;Android和H5混合开发;WebView点击H5界面跳转到Android原生界面。

    当时业务的需求是这样的,H5有一个活动商品列表的界面,IOS和Android共用这一个界面,点击商品可以跳转到Android原生的商品详情界面并传递商品ID:  大概就是点击H5界面跳转到Androi ...

    原生&plus;H5开发之:Android webview配置

    在上一篇文章Android 原生开发.H5.React-Native开发特点,我们可以了解到三种Android开发方式的区别和优缺点.[Android开发:原生+H5]系列的文章,将主要讲解Andro ...

    Android WebView常见问题及解决方案汇总

    Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变地展示我们的界面呢,这又涉及到了web app与native app之间孰优孰劣的争论. 于是 ...

    随机推荐

    Blue tooth

    一 . nordic BLE4.0 1.开发nordic的应用需要安装支持keil的pack库和插件 2.nordic的SDK很完整,实例涵盖了几乎所有的应用 https://www.nordicse ...

    SQL中Case的使用方法&lpar;下篇&rpar;(转)

    接上篇 四,根据条件有选择的UPDATE. 例,有如下更新条件 工资5000以上的职员,工资减少10% 工资在2000到4600之间的职员,工资增加15% 很容易考虑的是选择执行两次UPDATE语句, ...

    Jdk1&period;6 JUC源码解析&lpar;1&rpar;-atomic-AtomicXXX

    转自:http://brokendreams.iteye.com/blog/2250109 功能简介: 原子量和普通变量相比,主要体现在读写的线程安全上.对原子量的是原子的(比如多线程下的共享变量i+ ...

    C&plus;&plus;学习日记(二)————初始字符串类型

    使用频率高,但操作复杂的数据有哪些? 做下总结: int; double;float;char;bool这些类型用的比较频繁,但并不复杂.但对于字符串来说(char数组)用的频繁但操作又复杂,只能用一 ...

    UVA1627-Team them up&excl;(二分图判断&plus;动态规划)

    Problem UVA1627-Team them up! Total Submissions:1228  Solved:139 Time Limit: 3000 mSec Problem Descr ...

    进程命令ps&sol;top&sol;kill

    进程: 通俗的说就是 当前正在执行的一个程序 命令: ps 英文: process status 作用: 查看进程的详细状况 选项: a:显示终端上的所有进程,包括其他用户的进程 u:显示进程的详细状 ...

    yii中的restful方式输出并调用接口和判断用户是否登录状态

    //创建一个控制器接口 返回的是restful方式 <?php namespace frontend\controllers; use frontend\models\Fenlei; use f ...

    &lbrack;ML学习笔记&rsqb; XGBoost算法

    [ML学习笔记] XGBoost算法 回归树 决策树可用于分类和回归,分类的结果是离散值(类别),回归的结果是连续值(数值),但本质都是特征(feature)到结果/标签(label)之间的映射. 这 ...

    20155218《网络对抗》Exp8 Web基础

    20155218Exp8 Web基础 1.基础问题回答 1.什么是表单? 表单是一个包含表单元素的区域,表单元素是允许用户在表单中(比如:文本域.下拉列表.单选框.复选框等等) ...

    展开全文
  • 程序员必备的10个B站优质UP主!

    千次阅读 2021-06-15 17:28:58
    自我推荐一波,程序员必备的10个优质UP主! 1、Java学习营-海量Java学习视频,一套接一套,java程序员必备! 代表作品: 千锋教育Java教程_1000集零基础Java从入门到精通教程(保姆级教学视频)_哔哩哔哩_...

    推荐一波,B站上程序员必备的10个优质UP主!

     

    1、Java学习营-海量Java学习视频,一套接一套,java程序员必备!

    代表作品:https://www.bilibili.com/video/BV1FK4y1x7Ny

     

    2、大前端学习营-海量前端学习视频,一套接一套,前端开发者必备!

    代表作品:https://www.bilibili.com/video/BV17z4y1D7Yj

     

    3、千锋教育Python学院-海量Python学习视频,Pytho资源一网打尽,你想要的全都有!

    代表作品:https://www.bilibili.com/video/BV1R7411F7JV

     

    4、小狮教育-海量设计、影视剪辑、游戏原画学习视频,设计师必备!

    代表作品:https://www.bilibili.com/video/BV1Ng4y1v75i

     

    5、软件测试学习营-海量软件测试学习视频,0基础测试小白自学也能成大神!

    代表作品:https://www.bilibili.com/video/BV14v411B7p5

     

    6、网络安全学习营-海量网络安全学习视频,从入门到入狱,你值得拥有!

    代表作品:https://www.bilibili.com/video/BV1Lf4y1t7Mc

     

    7、云计算学习营-海量Linux云计算学习视频,免费名师干货分享,程序员必备!

    代表作品:https://www.bilibili.com/video/BV1pz4y1D73n

     

    8、物联网学习营-海量物联网学习视频,从0到1应有尽有,程序员必备!

    代表作品:https://www.bilibili.com/video/BV1FA411v7YW

     

    9、Unity3D游戏学习营-海量游戏开发学习视频,游戏开发者必备!

    代表作品:https://www.bilibili.com/video/BV1YT4y1L7Fw

     

    10、千锋教育- 千锋精品视频库,海量程序员免费学习视频,优秀程序员必备!

    除了以上的全套视频教程,10个UP主还发布了各学科各阶段的知识点视频教程,大家可以免费下载学习。

    在这里祝愿对IT感兴趣的小伙伴能如愿以偿学有所成,能成功踏入这个行业,找到一份理想的工作;想看免费视频和获取免费配套学习资料的同学可以持续关以上账号。

    展开全文
  • Linux C语言通过curl下载https资源代码如下,注意需要把main函数 char *url 赋值为需要下载文件的路径。 #include #include #include #include static size_t filesize = 0; size_t write_data(void *buffer, size_t...
  • k8s资源类型详解

    千次阅读 2020-12-20 13:03:21
    k8s资源类型一、k8s资源类型简介二、deployment资源类型三、service资源类型四、k8s资源的回滚操作五、用label控制pod的位置六、namespace简介七、pod资源类型八、健康检测的相关应用九、ReplicaSet的相关介绍十、...
  • 使用requests爬取b站的up主视频数据网页分析分析up主的 网页分析 分析up主的
  • CMIP5数据下载,详细介绍了CMIP5数据的下载步骤,共计25page。1. Getting an ESgF AccountChrome File Edit View Hlistory Bookmarks Window HlelpFill out userESGF PortalGcehttps://pcmdiG.Inl.gov/esgf-web-fe/...
  • 前言​前面两篇文章主要介绍了有关docker的基础概念、安装、以及对...一、docker管理资源机制——Control group​ Control group 是Linux内核提供的一种限制所使用物理资源的机制,这些资源主要是CPU、内存、blkio...
  • 资源下载集合(不定期更新)
  • 我们经常需要在github下载代码资源,或是修改代码为己所用,或是整合到系统中 但是,因为你懂的原因速度是真的慢 比如,你思考良久,下决心阅读pytorch代码,首先需要下载下来: git clone ...
  • 但有时在用brew下载某个包或者macbook系统大更新后很长时间没有更新使用homebrew,可能就会报错。 如果报错内容是提示brew的type,update等问题,可以输入: brew update-reset 然后再次向终端中输入brew,检查是否...
  • SM7250(高通5G)平台LCD bringup

    千次阅读 2021-04-22 00:13:58
    Lcd 初始化时序和init code下载流程确认 3.点亮顺序: 先点亮kernel,再点亮UEFI。Lcd的点亮工作2-3天之内完成,kernel点亮后,lcd功能正常,不会影响别的模块的开发 工作,uefi可以慢慢调试,尽量自己掌握工作的进度...
  • 在任何地点,我只需通过网页提交下载任务,家中的树莓派就会自动把我需要的资源,日夜不间断地下载到我的硬盘里~ 本文是详细的搭建步骤, 文末提供6个风评极好的优质种子站~ 首先安装Docker sudo apt update sudo ...
  • we are walking up, she walks.'% pat = 'walk(\w*) up'% regexprep(str, pat, 'ascend$1', 'tokenize')% returns 'I ascend, they ascended, we are ascending, she walks.'%% See also REGEXP, REGEXPI, STRREP, ...
  • state.py 8>按钮模块button.py 9>计分板模块scoreboard.py 二、游戏环境搭建 本练习使用了python3.5版本 pygame 模块安装,打开命令窗口,输入命令安装: python -m pip install pygame 游戏图片下载: URl:...
  • } /** if up key is pressed */ else if (evt.getKeyCode() == keys[UP]) { newKey = BUP; /** if only the down key is pressed */ if ((currentDirKeyDown & BDOWN) > 0 || ((currentDirKeyDown & BLEFT) == 0 &&...
  • 如果仍在创建 Deployment, 则输出类似于: NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx-deployment 3 0 0 0 1s 在检查集群中的 Deployment 时,所显示的字段有: NAME 列出了集群中 Deployment 的名称。...
  • From: Subject: =?gb2312?B?08NKYXZhyrXP1khUVFC2z7Xj0Pi0qy2/qrei1d/N+MLnLUphdmEt?==?gb2312?B?zOy8q1llc2t5?=Date: Sun, 7 Oct 2007 00:19:13 +0800MIME-Version: 1.0Content-Type: multipart/related;...
  • 实现文件下载的java代码//这是实现下载类(servlet),详细思路代码例如以下://也可连接数据库package com.message;import javax.servlet.*;import javax.servlet.http.*;import java.io.*;import java.util.*;public...
  • url = "http://11.111.111.11/up/character/" + id + ".png"; StartCoroutine(GetSpriteRequest(url, (response) => { //Debug.Log(" url :" + url); //Debug.Log(" response.name :" + response.name); asprite = ...
  • 当你需要下载大量图片的时候,或许你会去百度图片里一张张右键下载,但这样未免太麻烦了,有了这个工具,你直接运行下程序,输入你想要下载图片的关键字,然后输入你想要下载图片的数量,你就成功下载图片了!...
  • 打开文件夹记录逆势操作记录(前进后退) 支持拖拽操作:选中后拖拽,实现剪切到指定文件夹功能 快捷键操作:delete删除,ctrl+A全选,ctrl+C复制,ctrl+X剪切,up/down/left/right/home/end选择文件在线预览 支持...
  • 下载进度条演示文件大小未知长度已经下载0完成进度0%//文件长度var filesize=0;function $(obj) {return document.getElementById(obj);}//设置文件长度function setFileSize(fsize) {filesize=fsize;$("filesize")....
  • up[1].getChars(0, up[1].length(), pc, 0); PasswordAuthentication pa = new PasswordAuthentication(up[0], pc); return pa; } } private class xmlFilterInputStream extends InputStream { /** Creates a new ...
  • (6)按鼠标中键向前后推动或者按 Page Up、Page Down:放大、缩小。(7)小键盘上面的“+”和“-”,点选下面层选项:切换层。(8)A+T:顶对齐。A+L:左对齐。A+R:右对齐。A+B:底对齐。(9)Shift+S:单层显示与...
  • ue4官方文档下载及翻译

    千次阅读 2021-01-14 04:29:51
    Sections Montage Sections provide a way to break a Slot up into multiple portions of animation. Each Section has a name and location in the Slot's timeline. Using the name, you can either jump ...
  • scope false false Maps key to localized message and performs parametric replacement message org.apache.taglibs.standard.tag.rt.fmt.MessageTag JSP Message key to be looked up. key false true ...
  • 一、构建静态服务器1、使用express模块建立个js文件,命名server,内容代码如下://指定静态资源访问目录app.use(express.static(require('path').join(dirname,'public')));// app.use(express.static(require('...
  • 资源热更新以及代码热更新资源热更新流程lua代码热更新流程实现 注意,我使用的是2018版,如果是2019及以上的, lua的更新还需要多一步 可以看这个视频里的最后的部分 ...spm_id_from=pageDriver 资源热更新 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 92,196
精华内容 36,878
关键字:

up资源下载