通过appengine将文件上传到Google云端存储

我正在尝试将文件上传到Google云端存储。 我的Servlet代码是

public class UploadFile extends HttpServlet { private final String BUCKET = "XXXXXXXXX"; private boolean isMultipart; private String filePath; private int maxFileSize = 5 * 1024 * 1024; private int maxMemSize = 5 * 1024 * 1024; private File file ; public void init( ){ // Get the file location where it would be stored. filePath = getServletContext().getInitParameter("file-upload"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { // Check that we have a file upload request isMultipart = ServletFileUpload.isMultipartContent(request); response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter( ); if( !isMultipart ){ out.println(""); out.println(""); out.println("Servlet upload"); out.println(""); out.println(""); out.println("

No file uploaded

"); out.println(""); out.println(""); return; } DiskFileItemFactory factory = new DiskFileItemFactory(); // maximum size that will be stored in memory factory.setSizeThreshold(maxMemSize); // Location to save data that is larger than maxMemSize. factory.setRepository(new File("/temp/image/")); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory); // maximum file size to be uploaded. upload.setSizeMax( maxFileSize ); try{ // Parse the request to get file items. List fileItems = upload.parseRequest(request); // Process the uploaded file items Iterator i = fileItems.iterator(); out.println(""); out.println(""); out.println("Servlet upload"); out.println(""); out.println(""); while ( i.hasNext () ) { FileItem fi = (FileItem)i.next(); if ( !fi.isFormField () ) { // Get the uploaded file parameters String fieldName = fi.getFieldName(); String fileName = fi.getName(); String contentType = fi.getContentType(); boolean isInMemory = fi.isInMemory(); long sizeInBytes = fi.getSize(); // Write the file if( fileName.lastIndexOf("\\") >= 0 ){ file = new File( filePath + fileName.substring( fileName.lastIndexOf("\\"))) ; }else{ file = new File( filePath + fileName.substring(fileName.lastIndexOf("\\")+1)) ; } String path = Events.uploadFile ( fileName, "image/*", file, BUCKET ); // fi.write( file ) ; out.println("Uploaded Filename: " + fileName + "
"+ " File Path:"+ path); } } out.println(""); out.println(""); }catch(Exception ex) { System.out.println(ex); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { throw new ServletException("GET method used with " + getClass( ).getName( )+": POST method required."); } }

web.xml中

  UploadFile XXXXXXXXXX.UploadFile   UploadFile /uploadManager/UploadFile //Based on your original URL  

fileUpload函数将文件保存到GCS

 public static String uploadFile ( String name, String contentType, File file, String bucketName ) throws IOException, GeneralSecurityException { InputStreamContent contentStream = new InputStreamContent ( contentType, new FileInputStream ( file ) ); // Setting the length improves upload performance contentStream.setLength ( file.length () ); StorageObject objectMetadata = new StorageObject () // Set the destination object name .setName ( name ) // Set the access control list to publicly read-only .setAcl ( Arrays.asList ( new ObjectAccessControl ().setEntity ( "allUsers" ).setRole ( "READER" ) ) ); // Do the insert Storage client = StorageFactory.getService (); Storage.Objects.Insert insertRequest = client.objects ().insert ( bucketName, objectMetadata, contentStream ); insertRequest.execute (); return "https://storage.cloud.google.com/" + BUCKET + "/" + file.getName (); } 

但是,当我尝试使用某些API测试客户端进行测试时,它会出错

 org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found 

进一步将它与Angular中的UI集成并在本地测试后,我正面临着这个问题

 Cross-Origin Request Blocked Reason: CORS header 'Access-Control-Allow-Origin' missing 

我试图解决这个问题,但没有找到与google appengine相对应的解决方案。


最初我试图通过此代码上传图像,但在不久的将来,相同的代码将用于将.pdf和.html文件上传到GCS。

供参考:我正在使用Google Endpoints满足客户端的其他数据通信需求。 Client End是Angular中的webapp构建,但它将扩展到android和ios。

任何帮助将不胜感激。

谢谢

更新2016年1月8日

现在我在服务器上获取文件但我不知道在将文件发送到Google云端存储之前我必须暂时保存文件。 关于存储文件

 war\WEB-INI\ 

我面临的例外是

  java.security.AccessControlException: access denied ("java.io.FilePermission" "\war\WEB-INI\profile.png" "read") 

最后,我可以通过appengine将文件从客户端上传到Google云端存储。

我假设在执行这些步骤之前,您已准备好以下事项

  • 来自您的服务帐户的JSON文件。
  • 创建了默认存储桶。

第1步:制作一个像这样的Servlet

 package XXXXXXXXX; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; import java.util.Arrays; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.api.client.http.InputStreamContent; import com.google.api.services.storage.Storage; import com.google.api.services.storage.model.ObjectAccessControl; import com.google.api.services.storage.model.StorageObject; import XXXXXXXXXXXXX.StorageFactory; //@author Umesh Chauhan /** * Save File to GCS * * @param fileName File Name with format * @header Content-Type "*/*" * @return file path * @throws Exception Any Error during upload */ public class UploadFile extends HttpServlet { private static final long serialVersionUID = 1L; private final String BUCKET = "YOUR BUCKET NAME"; private int maxFileSize = 6 * 1024 * 1024; @Override protected void doOptions ( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { // pre-flight request processing resp.setHeader ( "Access-Control-Allow-Origin", "*" ); resp.setHeader ( "Access-Control-Allow-Methods", "*" ); resp.setHeader ( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); } @Override public void doPost ( HttpServletRequest request, HttpServletResponse response ) throws ServletException, java.io.IOException { try { String path = uploadFile ( request.getParameter ( "fileName" ), request.getContentType (), request.getInputStream (), BUCKET, request.getInputStream ().available () ); // Sending Response response.setStatus ( HttpServletResponse.SC_OK ); response.getWriter ().write ( path ); response.getWriter ().flush (); response.getWriter ().close (); } catch ( GeneralSecurityException e ) { e.printStackTrace (); } } public String uploadFile ( String name, String contentType, InputStream input, String bucketName, int contentLength ) throws IOException, GeneralSecurityException { InputStreamContent contentStream = new InputStreamContent ( contentType, input ); if ( contentLength < maxFileSize ) { // It is done Automatically. /* * // Setting the length improves upload performance * contentStream.setLength ( contentLength ); */ StorageObject objectMetadata = new StorageObject () // Set the destination object name .setName ( name ) // Set the access control list to publicly read-only .setAcl ( Arrays.asList ( new ObjectAccessControl ().setEntity ( "allUsers" ).setRole ( "READER" ) ) ); // Do the insert Storage client = StorageFactory.getService (); Storage.Objects.Insert insertRequest = client.objects () .insert ( bucketName, objectMetadata, contentStream ); insertRequest.execute (); return "https://storage.cloud.google.com/" + BUCKET + "/" + name; } else { throw new GeneralSecurityException ( "File size canot be more then 6 MB !" ); } } public void doGet ( HttpServletRequest request, HttpServletResponse response ) throws ServletException, java.io.IOException { throw new ServletException ( "GET method used with " + getClass ().getName () + ": POST method required." ); } } 

第2步:您的存储工厂

 package XXXXXXXXXXXX; import java.io.IOException; import java.net.URL; import java.security.GeneralSecurityException; import java.util.Collection; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.storage.Storage; import com.google.api.services.storage.StorageScopes; //@author Umesh Chauhan public class StorageFactory { private static Storage instance = null; public static synchronized Storage getService () throws IOException, GeneralSecurityException { if ( instance == null ) { instance = buildService (); } return instance; } private static Storage buildService () throws IOException, GeneralSecurityException { HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport (); JsonFactory jsonFactory = new JacksonFactory (); GoogleCredential credential = GoogleCredential.fromStream ( new URL ( "HERE GOES THE URL FOR YOUR SERVICE ACCOUNT JSON - I USED GOOGLE DRIVE DIRECT DOWNLOAD LINK TO MY JSON FILE" ) .openStream () ); // Depending on the environment that provides the default credentials // (for // example: Compute Engine, App Engine), the credentials may require us // to // specify the scopes we need explicitly. Check for this case, and // inject // the Cloud Storage scope if required. if ( credential.createScopedRequired () ) { Collection scopes = StorageScopes.all (); credential = credential.createScoped ( scopes ); } return new Storage.Builder ( transport, jsonFactory, credential ).setApplicationName ( "YOUR PROJECT NAME" ).build (); } } 

第3步:更新web.xml

   UploadFile PACKAGE.UploadFile   UploadFile /uploadManager/UploadFile //Based on your original URL  

以下是根据文档上传到存储的正确方法

 import com.google.appengine.tools.cloudstorage.*; public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String buffer = "the data to be saved in GCS bla bla"; GcsFileOptions instance = GcsFileOptions.getDefaultInstance(); GcsFilename fileName = new GcsFilename("bucketName", "fileName"); GcsOutputChannel outputChannel; gcsService.createOrReplace(fileName, instance, ByteBuffer.wrap(buffer.getBytes())); } 

你会在这里找到完整的代码

首先,您需要在飞行前请求处理中解决CORS问题,您需要在后端执行:在Google App Engine上通过添加doOptions方法完成,例如:

 @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Access-Control-Allow-Origin", "*"); resp.setHeader("Access-Control-Allow-Methods", "*"); resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); } 

那么你需要确保你发送带有Content-Type: multipart/form-data标题的请求,否则你的文件将被错误地编码。 在angular2请求标头中,您的post请求中的第三个(可选)参数设置为:

 let headers = new Headers(); headers.append('content-type', 'multipart/form-data'); http.post(url, body, { headers:headers }) 

对不起,这并没有直接解决你的问题,但无论如何我想指出来。 我建议使用您的一个正常端点生成一个临时上传URL,Angular客户端可以使用该URL将文件直接发送到云存储,而无需通过您的应用程序。 一种方法是通过blobstore API,如此处所述。 您可以按照此处所述(在同一页面下方)通过该API上传到云存储。

这样可以减少服务器上所需的上传代码量,不受GAE请求的32MB限制,并且符合Google的建议(如上面的文档链接所示)。

使用com.google.cloud.storage

  • 在appengine上运行时 – 无需身份validation
  • 在本地运行时运行gcloud auth application-default login命令,请参阅身份validation

     import com.google.cloud.storage.*; public static void upload(String bucketName, String fileId, String content) throws IOException { Storage storage = StorageOptions.newBuilder().build().getService(); BlobInfo fileInfo = BlobInfo.newBuilder(bucketName, fileId) .build(); InputStream fileIS = IOUtils.toInputStream(content, "UTF-8"); storage.create(fileInfo, fileIS); }